【Java 8实战笔记】为什么要关心Java 8

概述部分

使用Java8新的API,流(Stream),它支持许多处理数据的并行操作,由Stream库来选择最佳低级执行机制可以避免使用Synchronized编写代码,这种代码不仅容易出错,并且在多核CPU上的执行成本也很高。
因为多核CPU每个处理器内核都有独立的高速缓存。加锁需要这些高速缓存同步运行,而这又需要内核间进行缓慢的缓存一致性协议通信

Streams的作用不仅仅是把代码传递给方法,它提供了一种新的间接地表达行为参数化的方法。比如,对于两个只有几行代码不同的方法,只需要把不同的那部分代码作为参数传递进去就可以。

流处理

流是一系列数据项,一次只生成一项。
程序从输入流中一个一个读取数据项。以同样的方式将数据项写入输出流。

基于Unix操作流的思想,Java 8在java.util.stream中添加了一个Stream APIStream API的很多方法可以链接起来形成一个复杂的流水线。
Java 8可以透明的把输入的不相关部分拿到几个CPU内核上去分别执行Stream操作流水线,并不需要Thread

Unix操作流示例:
cat file1 file2 | tr "[A-Z]" "[a-z]" | sort | tail -3 将两个文件连接起来创建一个流,tr会转换流中的字符,sort会对流中的行进行排序,而tail -3则给出流的最后三行,这些程序通过管道(|)连接在一起。

用行为参数化把代码传递给方法

Java 8增加了通过API来传递代码的能力,把方法(代码)作为参数传递给另一个方法。

并行与共享的可变数据

行为要能够同时对不同的输入安全地执行,这意味着写代码不能访问共享的可变数据。
Java8的流实现并行比Java现有的线程API更容易,虽然可以使用Synchronized来打破“不能有共享的可变数据”的规则,但是同步迫使代码按照顺序执行,这与并行处理相悖。

没有共享的可变数据将方法和函数(即代码)传递给其他方法的能力是函数式编程范式的基石。

不能有共享的可变数据的要求意味着,一个方法可以通过它将参数值转换为结果的方法完全描述的,就像是一个数学函数,没有可见的副作用。

Java中的函数

Java 8的函数指的是 方法,尤其是静态方法,没有副作用的函数。

Java 可能操作的值: 原始值,对象(严格来说是对象的引用)。其他的结构(二等公民)虽然有助于表示值的结构,但它们程序执行期间并不能传递。程序之间期间能传递的是(一等公民)。虽然用方法来定义类,类可以通过实例化产生值,但是类和方法本身都不是值。人们又发现,通过在运行时传递方法*能够将方法变成值来传递。

让方法等概念作为值(一等公民)会让编程变得容易很多。

方法和Lambda作为一等公民

Java 8的一个新功能是方法引用

比如要编写一个方法,然后给它一个File,它会告诉你文件是不是隐藏的,在Java 8之前可能要这么写:

File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
    public boolean accept(File file) {
        return file.isHidden();
    }
});

有点绕,在Java8里,可以把代码重写成这个样子:

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

要将方法作为值传给另一个方法,只需用方法引用::语法(即“把这个方法作为值”)将其传给另一个方法即可(这里还使用了函数代表方法)。

这样做的好处是代码读起来更接近问题的陈述,方法生成升为了“一等公民”。

对象引用传递对象类似(对象引用使用new创建),当写下XXX::MethodName的时候,就创建了一个方法引用,同样也可以传递它。以前的Java版本只能把方法包裹在FileFilter对象里,然后才能传递给别的方法。

Lambda --- 匿名函数

除了允许函数成为一等值外,Java 8还体现了更广义的将函数作为值的思想------Lambda(匿名函数)

函数式编程风格,即“编写把函数作为一等值来传递的程序”

传递代码的例子

假设有一个Apple类,它有一个getColor方法,还有一个变量inventory保存着一个Apples的列表。要选出所有的绿苹果并返回一个列表。在Java 8之前,可能会写一个这样的方法:

public static List<Apple> filterGreenApples(List<Apple> inventory){
    List<Apple> result = new ArrayList<>();
    for (Apple apple: inventory){
        //代码会仅仅选出绿色苹果,并加入result中
        if ("green".equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

接下来,又想要选出重的苹果,比如超过150克的,于是又写了如下方法:

public static List<Apple> filterGreenApples(List<Apple> inventory){
    List<Apple> result = new ArrayList<>();
    for (Apple apple: inventory){
        //代码会仅仅选出重量大于苹果,并加入result中
        if (apple.equals(apple.getWeight() > 150 ) {
            result.add(apple);
        }
    }
    return result;
}

这两个方法只有一行不同,Java 8由于可以把条件代码作为参数传递进去,这样可以避免filter方法出现重复的代码。于是可以这样写:

    public static boolean isGreenApple(Apple apple) {
        return "green".equals(apple.getColor()); 
    }

    public static boolean isHeavyApple(Apple apple) {
        return apple.getWeight() > 150;
    }

    public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(p.test(apple)){
                result.add(apple);
            }
        }
        return result;
    }       

注意filterApples的参数: Predicate<Apple> p,这里是从java.util.function.predicate导入的,它的作用是定义一个泛型接口:

public interface Predicate<T> {
    boolean test(T t);
}

通过这样,可以将方法通过谓语(Predicate)参数p传递进filterApples

要使用 filterApples 的话,可以写成这样:

List<Apple> greenApples = filterApples(inventory, FilteringApples::isGreenApple);

或者

 List<Apple> heavyApples = filterApples(inventory, FilteringApples::isHeavyApple);

Predicate(谓语)在数学上常用来代表一个类似函数的东西,它接受一个参数值,并返回true或false。

从传递方法到Lamda

除了把方法作为值来传递以外,Java 8还引入了一套记法(匿名函数或Lambda),于是上面的代码可以写成:

List<Apple> greenApples2 = filterApples(inventory, 
(Apple a) -> "green".equals(a.getColor()));

或者

List<Apple> heavyApples2 = filterApples(inventory, 
(Apple a) -> a.getWeight() > 150);

甚至

List<Apple> weirdApples = filterApples(inventory,
 (Apple a) -> a.getWeight() < 80 || "brown".equals(a.getColor()));

通过这种方法,你甚至不需要为只用一次的方法写定义;代码更干净清晰。

Q:什么时候使用方法作为值传递,什么时候使用Lambda?

A:要是代码的长度多于几行,使用Lambda表示的效果并不是一目了然,这样还是应该使用方法引用来指向一个方法,而不是使用匿名的Lambda。简言之,使用哪种方法应该以代码的清晰度为准绳。

通过Stream和Lamdba表达式,可以将之前的筛选代码改进成如下形式:

import static java.util.stream.Collector.toList;
List<Apple> heavyApples =
  inventory.stream().filter((Apple a) -> a.getWeight() > 150).collect(toList());

并行处理:

import static java.util.stream.Collector.toList;
List<Apple> heavyApples =
  inventory.parallelstream().filter((Apple a) -> a.getWeight() > 150).collect(toList());

和Collection API相比,Stream API处理数据的方式非常不同,用集合需要自己去做迭代的过程。你需要用for-each循环一个个去迭代元素、处理元素,这种数据迭代的方法被称为外部迭代。而有了Stream API,数据处理完全在库内部进行,这种迭代思想被称为内部迭代。

Java 8用Stream API解决了两个问题:

  1. 集合处理时的套路和晦涩
  2. 难以利用多核

Colletion主要是为了存储和访问数据,而Stream则主要用于描述对数据的计算。关键点在于,Stream允许并提倡并行处理一个Stream中的元素。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容

  • 第一章 为什么要关心Java 8 使用Stream库来选择最佳低级执行机制可以避免使用Synchronized(同...
    谢随安阅读 1,479评论 0 4
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,554评论 18 139
  • 原链接:http://www.cnblogs.com/langtianya/p/3757993.html JDK各...
    把爱放下会走更远阅读 1,104评论 0 10
  • Java8 in action 没有共享的可变数据,将方法和函数即代码传递给其他方法的能力就是我们平常所说的函数式...
    铁牛很铁阅读 1,199评论 1 2
  • 如果正值梅雨,我撑着伞走在大街上。与一个姑娘擦肩而过的时候,我鬼使神差地回了头:“姑娘,怎么哭得这样惨?”眼泡肿着...
    小木山庄的溜溜阅读 329评论 1 5