Java 8 中如何优雅的处理集合
Java 8 中如何优雅的处理集合(Stream API)
在Java中,集合和数组是我们经常会用到的数据结构,需要经常对他们做增、删、改、查、聚合、统计、过滤等操作。相比之下,关系型数据库中也同样有这些操作,但是在Java 8之前,集合和数组的处理并不是很便捷。
不过,这一问题在Java 8中得到了改善,Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。本文就来介绍下如何使用Stream。特别说明一下,关于Stream的性能及原理不是本文的重点,如果大家感兴趣后面会出文章单独介绍。
一、Stream介绍
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。
Stream有以下特性及优点:
无存储。Stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
为函数式编程而生。对Stream的任何修改都不会修改背后的数据源,比如对Stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新Stream。
惰式执行。Stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
可消费性。Stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
我们举一个例子,来看一下到底Stream可以做什么事情:

上面的例子中,获取一些带颜色塑料球作为数据源,首先过滤掉红色的、把它们融化成随机的三角形。再过滤器并删除小的三角形。最后计算出剩余图形的周长。
如上图,对于流的处理,主要有三种关键性操作:分别是流的创建、中间操作(intermediate operation)以及最终操作(terminal operation)。
二、Stream的创建
在Java 8 中,可以有多种方法来创建流。
1、通过已有的集合来创建流
在Java 8中,除了增加了很多Stream相关的类以外,还对集合类自身做了增强,在其中增加了stream方法,可以将一个集合类转换成流。
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis", "Hello", "HelloWorld", "Hollis");
Stream<String> stream = strings.stream();
以上,通过一个已有的List创建一个流。除此以外,还有一个parallelStream方法,可以为集合创建一个并行流。
这种通过集合创建出一个Stream的方式也是比较常用的一种方式。
2、通过Stream创建流
可以使用Stream类提供的方法,直接返回一个由指定元素组成的流。
Stream<String> stream = Stream.of("Hollis", "HollisChuang", "hollis", "Hello", "HelloWorld", "Hollis");
如以上代码,直接通过of方法,创建并返回一个Stream。
三、Stream中间操作
Stream有很多中间操作,多个中间操作可以连接起来形成一个流水线,每一个中间操作就像流水线上的一个工人,每人工人都可以对流进行加工,加工后得到的结果还是一个流。

以下是常用的中间操作列表:

filter
filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤掉空字符串:
List<String> strings = Arrays.asList("Hollis", "", "HollisChuang", "H", "hollis");
strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::println);
//Hollis, , HollisChuang, H, hollis
map
map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().map( i -> i*i).forEach(System.out::println);
//9,4,4,9,49,9,25
limit/skip
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素。以下代码片段使用 limit 方法保理4个元素:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().limit(4).forEach(System.out::println);
//3,2,2,3
sorted
sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法进行排序:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().sorted().forEach(System.out::println);
//2,2,3,3,3,5,7
distinct
distinct主要用来去重,以下代码片段使用 distinct 对元素进行去重:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().distinct().forEach(System.out::println);
//3,2,7,5
接下来我们通过一个例子和一张图,来演示下,当一个Stream先后通过filter、map、sort、limit以及distinct处理后会发生什么。
代码如下:
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis", "Hello", "HelloWorld", "Hollis");
Stream s = strings.stream().filter(string -> string.length()<= 6).map(String::length).sorted().limit(3)
.distinct();
过程及每一步得到的结果如下图:

四、Stream最终操作
Stream的中间操作得到的结果还是一个Stream,那么如何把一个Stream转换成我们需要的类型呢?比如计算出流中元素的个数、将流装换成集合等。这就需要最终操作(terminal operation)
最终操作会消耗流,产生一个最终结果。也就是说,在最终操作之后,不能再次使用流,也不能在使用任何中间操作,否则将抛出异常:
java.lang.IllegalStateException: stream has already been operated upon or closed
俗话说,“你永远不会两次踏入同一条河”也正是这个意思。
常用的最终操作如下图:

forEach
Stream 提供了方法 'forEach' 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
count
count用来统计流中的元素个数。
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis","Hollis666", "Hello", "HelloWorld", "Hollis");
System.out.println(strings.stream().count());
//7
collect
collect就是一个归约操作,可以接受各种做法作为参数,将流中的元素累积成一个汇总结果:
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis","Hollis666", "Hello", "HelloWorld", "Hollis");
strings = strings.stream().filter(string -> string.startsWith("Hollis")).collect(Collectors.toList());
System.out.println(strings);
//Hollis, HollisChuang, Hollis666, Hollis
接下来,我们还是使用一张图,来演示下,前文的例子中,当一个Stream先后通过filter、map、sort、limit以及distinct处理后会,在分别使用不同的最终操作可以得到怎样的结果。
下图,展示了文中介绍的所有操作的位置、输入、输出以及使用一个案例展示了其结果。

五、总结
本文介绍了Java 8中的Stream 的用途,优点等。还接受了Stream的几种用法,分别是Stream创建、中间操作和最终操作。
Stream的创建有两种方式,分别是通过集合类的stream方法、通过Stream的of方法。
Stream的中间操作可以用来处理Stream,中间操作的输入和输出都是Stream,中间操作可以是过滤、转换、排序等。
Stream的最终操作可以将Stream转成其他形式,如计算出流中元素的个数、将流装换成集合、以及元素的遍历等。
Java 8 中如何优雅的处理集合的更多相关文章
- Java 8中用法优雅的Stream,性能也"优雅"吗?
之前的文章中我们介绍了Java 8中Stream相关的API,我们提到Stream API可以极大提高Java程序员的生产力,让程序员写出高效率.干净.简洁的代码. 那么,Stream API的性能到 ...
- Java 8 中的 Streams API 详解
为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...
- Java 8中的 Streams API 详解
为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...
- (转)Java 8 中的 Streams API 详解
为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...
- Java 8中处理集合的优雅姿势——Stream
在Java中,集合和数组是我们经常会用到的数据结构,需要经常对他们做增.删.改.查.聚合.统计.过滤等操作.相比之下,关系型数据库中也同样有这些操作,但是在Java 8之前,集合和数组的处理并不是很便 ...
- java多线程中并发集合和同步集合有哪些?区别是什么?
java多线程中并发集合和同步集合有哪些? hashmap 是非同步的,故在多线程中是线程不安全的,不过也可以使用 同步类来进行包装: 包装类Collections.synchronizedMap() ...
- Java 8 Lambda 表达式及 Stream 在集合中的用法
简介 虽然 Java 8 已经发布有一段时间了,但是关于 Java 8 中的 Lambda 表达式最近才开始系统的学习,刚开始就被 Stream 的各种骚操作深深的吸引住了,简直漂亮的不像 Java. ...
- java中Comparator的用法 -- 实现集合和数组排序
在java中,如果要对集合对象或数组对象进行排序,需要实现Comparator接口以达到我们想要的目标. 接下来我们模拟下在集合对象中对日期属性进行排序 一.实体类Step package com.l ...
- 避免 Java 代码中的“坏味道”
1.需要 Map 的主键和取值时,应该迭代 entrySet() 当循环中只需要 Map 的主键时,迭代 keySet() 是正确的.但是,当需要主键和取值时,迭代 entrySet() 才是更高效的 ...
随机推荐
- .NET平台上的编译器不完全列表(转别)
http://www.cnblogs.com/william_fire/archive/2005/05/15/155800.html最近因为开发需要,要研究一下.NET上基于C#扩展的编译器实现的框架 ...
- 15个 MySQL 基础面试题,DBA 们准备好了吗?
此前我们已经有发表过Linux 面试基础问答之一.二和三共3篇文章,获得读者的好评,同时我们得到反馈,有些读者希望这种交互式学习方法能够做得更加灵活.心动不如行动,我们这就为您奉上 15个 MySQL ...
- Excel中拆分列
常常在linux下我们写个shell,结果输出至txt中需要把数据一条一条的抠出来,也是很累人的事情,而直接输出值excel中的话相对较简单,但是会集中在第一列.这时候也不用担心,在Excel中选择第 ...
- LNMP企业应用部署全过程(基于DEDE后台)
系统环境说明: os:centos 5.6 nginx:nginx-1.0.11 php:php-5.3.9 mysql:mysql-5.5.20 文档导读: 一 准备工作 二 基础软件包安装 三 安 ...
- 02-线性结构4 Pop Sequence
02-线性结构4 Pop Sequence (25分) 时间限制:400ms 内存限制:64MB 代码长度限制:16kB 判题程序:系统默认 作者:陈越 单位:浙江大学 https://pta.p ...
- Recursion and System Stack
递归是计算机科学中一个非常重要的概念,对于斐波那契那种比较简单的递归,分析起来比较容易,但是由于二叉树涉及指针操作,所以模仿下遍历过程中系统栈的情况. 以二叉树中序遍历为例演示: //二叉树定义 st ...
- 程序员还在用360,腾讯电脑管家清理注册表,清理垃圾?只能说你太low
首先明明电脑上,就有清理垃圾和无用注册表的功能,只是我么缺少发现美的眼睛. 为什么不用360,腾讯全家桶. 那玩意固然香,而且真香,但是后台占用率太高,作为一个有洁癖的我,实在是不想看到自己右下角多一 ...
- Windows 10配置VS Code C++环境(超详细,面向小白以及大佬们)
看完这个,还有下一篇:门在这 我看了网上的大佬们配的我是在是看不懂啊?我是一个小白啊?这太难了,这阻挡不了我,想使用这很骚的IDE,于是在不断的摸索下,终于配置成功,小白们也不用慌,这次非常简单.一定 ...
- 一只简单的网络爬虫(基于linux C/C++)————支持动态模块加载
插件在软件设计中有很大的好处,可以方便地扩展各种功能,使用插件技术能够在分析.设计.开发.项目计划.协作生产和产品扩展等很多方面带来好处: (1)结构清晰.易于理解.由于借鉴了硬件总线的结构,而且各个 ...
- CTF-Pwn-[BJDCTF 2nd]diff
CTF-Pwn-[BJDCTF 2nd]diff 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!本文仅用于学习与交流,不得用于非 ...