学习 Java8 函数式编程 (三)

扯淡


在准备学 Java8 之前,我以为不会很难。所以,我就决定一边学 Java8,一边写博客。当我准备写这篇博客的时候,我发现两件都不容易。如果不是我亲身体验,我也没法知道一篇博客的背后,作者得付出多少时间和精力。这会让我在读每一篇博客时,保持对作者的敬畏之心。

本篇博客会简单介绍 Stream,以及认识是用 Stream 的 API。当我不知道这篇博客该怎么写的时候,我想试着从 Stream 的源码去解读。然后,我就傻逼似的,屁颠屁颠地去看 Stream 源码,结果可想而知,我抱着头顶上的一堆星星去床上思考人生了。Stream 的功能非常强大,给我们这些普通的程序员带来了极大的方便。这必然意味着,Java 的布道师们会在底层实现一堆逻辑非常复杂的代码。所以,学习一门新技术的时候,我们应该先知道这门技术是什么,可以干什么,然后熟练掌握,最后深究它背后的实现原理。

认识 Stream


第一眼看见 Stream 这个词的时候,会误认为和 Java 的 I/O 流有着不可描述的关系。其实,它们俩就像潘长江和曾志伟并不是兄弟一样,毫无关系。

其实和 Stream 有关系的是 Java 中的容器(我们经常使用的 List 和 Set 等集合)。在 Java8 之前,我们使用的是增强 for 语法糖和容器自身的 Iterator 来遍历数据。我们先来看下清单 1 中的代码,使用增强 for 和 Iterator 来遍历集合。

清单 1

List<String> strList = 
    Arrays.asList("a", "b", "c", "d");

//使用增强 for 遍历集合
for (String str : strList) {
    System.out.println(str);
}

//使用 Iterator 遍历集合
Iterator iterator = strList.iterator();
while (iterator.hasNext()) {
    String str = iterator.next();
    System.out.println(str);
}

清单 1 中的代码使用了增强 for 语法糖和 Iterator 来遍历一个集合。其实使用增强 for 和 Iterator 是一回事。因为增强 for 是 Java 为简化我们使用 Iterator 而提供的一个语法糖,它背后的实现依然是 Iterator。大家可能会发现,无论我们是使用增强 for 还是 Iteraotr 遍历集合,都需要将集合中的元素一个个的取出来。这种将元素从集合中取出来的迭代方式叫外部迭代

现在,我们来看下清单 2 中的代码,使用 Java8 中的 Stream 来遍历集合。

清单 2

List<String> strList = 
    Arrays.asList("a", "b", "c",  "d");
strList.stream()
    .forEach(str -> System.out.println(str));

当大家读完清单 2 中的代码后,是不是似乎有点知道 Stream 是干啥的了。通过请单 2 中的代码,我们只能看出 Stream 有遍历的集合功能,而且写法更加简单,代码的可读性也增强了。使用 Stream 遍历集合的方式叫内部迭代

Java8 的布道师们利用了面向对象的思想,对容器的存储数据和遍历数据进行了解耦。从 Java8 开始,在绝大多数情况下,我们都会使用 Stream 来遍历集合,容器只用来存储数据,无需关心遍历。

按我的理解,Stream 就是一种新的迭代方式,它以一种更加简单的方式对数据进行处理,让代码简洁易懂。在接下来的内容中,大家会发现使用 Stream 处理数据的代码,我们能够清晰读出代码的意图,而且代码中没有多余的临时变量和命令式代码。同时,使用 Stream 操作数据,并不会改变数据源(可以是容器,可以是数组,也可以是其他类型的数据源)。在多核时代,我们可以使用 Stream 写出高效以及线程安全的代码。但这不意味着我们需要写线程相关的代码,因为 Stream 在底层已经帮我们实现了。

在接下来使用 Stream 的过程中,我们只需要在 Stream 的每个函数中传入一个函数,传入的函数只是用来告诉 Stream 需要做什么,具体该如何做,并不需要我们关心。

使用 Stream API


准备三个类

在使用 Stream API 之前,我们先准备三个类 Artist (创作音乐的个人或团队),Track (专辑中的一些曲目),Album (专辑,由若干曲目组成)。接下来的内容,都会围绕这三个类展开。这三个类的具体定义如清单 3, 4, 5 所示。

清单 3

public class Artist {
    
    //艺术家的名字(例如 “纵贯线乐队”)
    private String name;
    
    //乐队成员(例如 “周华健"),该字段可为空
    private String members;
    
    //乐队来自哪里(例如 “台湾”)
    private String origin;
    
    //省略 set 和 get 方法
             
}

清单 4

public class Track {
    //曲目名称
    private String name;
    
    //省略 set 和 get 方法

}

清单 5

public class Album {
    
    //专辑名
    private String name;
    
    //专辑上所有曲目的列表
    private List<Track> tracks;
    
    //参与创作本专辑的艺术家列表
    private List<Artist> musicians;
    
    //省略 set 和 get 方法
        
}

案例描述

问题是,找出某张专辑上所有乐队的国籍。艺术家列表里既有个人,也有乐队,其中乐队名以 The 开头。

该问题可分解为如下几个步骤:

  • 找出专辑上的所有表演者
  • 分辨出哪些表演者是乐队
  • 找出每个乐队的国籍
  • 将找出的国籍放入一个集合

使用命令式代码实现

我们先是使用命令式风格的代码实现上述案例,代码如清单 6 所示。

清单 6

//将专辑中的艺术家为乐队的单独放入一个集合 bankList
List<Artist> artistList = album.getMusicians();
List<Artist> bankList = new ArrayList<>();
for (Artist artist : artistList) {
    if (artist.getName().startsWith("The")) {
        bankList.add(artist);
    }
}
        
//找出bankList中每个乐队的国籍,并将国籍放入originList
List<String> originList = new ArrayList<>();
for (Artist artist : bankList) {
    originList.add(artist.getOrigin());
}            

清单 6 的代码存在如下问题:

  • 存在多余的临时变量
  • 样板式代码掩盖了关键代码,代码的可读性很低

使用 Stream API 实现

我们现在使用 Stream API 来重写清单 6 中的代码,如清单 7 所示。

清单 7

Set<String> originList =
    album.getMusicians()
    .stream()
    .filter(artist -> artist.getName().startsWith("The"))
    .map(artist -> artist.getOrigin())
    .collect(toSet());

当大家看到清单 7 的代码时候,有没有很惊喜?反正我是为之跳跃。我先来解读下这段代码。其实,当大家熟悉 Stream 的 API 后,一眼就能看出这段代码的意图,它简直就是将问题的每一个小步骤描述了一遍,没有一点拖泥带水。首先通过 getMusicians 获取 album (专辑) 中的艺术家列表;然后使用艺术家列表构建一个 Stream,这里要说的是,所有 Collection 的子类都可以使用 stream 方法来构建一个 Stream,因为 Java8 允许接口中有 default 方法;接着调用 Stream 的 filter 方法,并告诉它筛选出 Stream 中 艺术家名字以 The 开头的数据,将筛选出的数据组织成一个新的 Stream 返回;紧接着,调用 Stream 的 map 方法,将 Stream 中的艺术家映射为艺术家的国籍,返回新的 Stream;最后,使用 Stream 的 collect 方法生成一个 Set 集合。

经过清单6 中的代码与清单 7 中的代码进行比较,我想大家会一致认同,Stream 简直太好用了,写出来的代码简洁易懂。

结束语

本篇博客只是简单介绍了 Stream 和 使用 Stream 解决问题的具体案例,在后续博客中,我会更加细致地介绍 Stream。

彩蛋


我今天要给大家介绍的是,我的学长勇哥,这个是他在开源中国的地址 https://my.oschina.net/silence88。勇哥是个完美的 Java 程序员,人帅,会做菜,弹的一手好吉他(他就是因为吉他与我嫂子相识的),会打篮球,最牛逼的还是写代码厉害。勇哥的博客值得一读,你们会看到一个屌丝程序员是如何打怪升级的。勇哥是在大四的第一个学期开始自学 Java 的,他现在顺丰的丰巢就职。

感谢大家的阅读~

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,600评论 18 139
  • Jav8中,在核心类库中引入了新的概念,流(Stream)。流使得程序媛们得以站在更高的抽象层次上对集合进行操作。...
    仁昌居士阅读 3,619评论 0 6
  • Java8 in action 没有共享的可变数据,将方法和函数即代码传递给其他方法的能力就是我们平常所说的函数式...
    铁牛很铁阅读 1,213评论 1 2
  • 文/徐小木 2017-03-12 "生而为人,我们或多或少都带着点俗气,它是我们骨子里无法剔除的部分。...
    徐小木阅读 715评论 3 4
  • 新学期把电脑系统格了重新安装配置环境,windows下用的PhpStudy16种组合的Nginx+PHP7.0 n...
    小小奶狗阅读 1,205评论 0 0