stream
Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。
Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
stream特性
stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
stream具有延迟执行特性(惰性求值),只有调用终端操作时,中间操作才会执行。
stream是一次性的(一旦一个流对象经过一个终结操作后,这个流就不会被使用)
debug中可以看到流的执行过程。
常用操作
创建流
中间操作
每次返回一个新的流,可以有多个
终结操作
每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
ctrl+alt+m可以自动封装为函数
创建流的方式
集合对象
集合对象.stream()
数组
Arrays.stream(数组)
Integer arr[]={1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(arr);
stream.distinct().forEach(integer -> System.out.println(integer));
双列集合
先转换为单列集合再创建
map.entrySet().stream();
Map<String, Integer> map = new HashMap<>();
map.put("蜡笔",11);
map.put("蜡笔2",11);
map.put("蜡笔3",11);
Set<Map.Entry<String, Integer>> entries = map.entrySet();
entries.stream().distinct().forEach(stringIntegerEntry -> System.out.println(stringIntegerEntry));
stream中的常用API中间操作
filter
筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。
map与flatMap
flatMap
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
举例说明
比如一个作者类里有
然后需要输出所有作者的所有书籍。
传统需要双重for循环,
flatMap将作者流中的值转换为书籍流,然后将所有书籍流组合成一个大流。
authors.stream()
.flatMap((Function<Author, Stream<Book>>) author -> author.getBooks().stream())
.distinct()
.forEach(book -> System.out.println(book.getName()));
获取所有分类
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book -> Arrays.stream(book.getCategory().split(",")))
.distinct()
.forEach(s -> System.out.println(s));
先将作者流转换为书籍流,然后将书籍流转换为分类流,最后对分类流进行操作。
二者区别
可以看出flatmap和map的参数差别在于,
map传入一个传入实体返回实体,
flatMap是传入实体返回的却是Stream流,
那既然是流,那么最好返回值本身是一个Stream,或者能被转换成Stream的对象!
flatmap的作用 —— 把嵌套集合,按照子集合的形式,
统一放入到新的一个集合中去,这就叫结果展平
例如,一个年级的学生,按照班级为单位,如今年段长想统计该年段所有的学生信息,一个flatmap就能轻松搞定,无需再for循环取遍历获取了~
distinct
可以去除流中的重复元素,底层依赖的是Object的equals方法来判断是否是相同对象。所以需要重写equals方法。
sorted
sorted,实现排序,中间操作。
如果调用空参的
实体类需要实现Comparable接口
重写方法,实现排序
调用有参的
需要重写里面的方法
limit
可以设置流的最大长度,超出的部分将被抛弃
例如:打印年龄最大的两个作者的姓名。
authors.stream()
.distinct()
.sorted()
.limit(2)
.forEach(author -> System.out.println(author.getName()));
skip
跳过流中的前n个元素了,返回剩下的元素。
authors.stream()
.sorted()
.skip(1)
.forEach(author -> System.out.println(author.getName()));
stream中常用API终结操作
forEach
对流中的元素进行遍历操作,我们通过传入参数去指定对遍历的元素进行什么具体操作。
count
可以获取流中元素的个数
//打印这些作家的所出书籍的数目,去重
long count = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.count();
System.out.println(count);
max与min
可以用来获取流中的最值
//获取作品中的最高分和最低分
Optional<Integer> max = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.max((o1, o2) -> o1 - o2);
System.out.println(max.get());
collect
把当前流转换为一个集合
例.获取一个存放所有作者名字的List集合
List<String> collect = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toList());
System.out.println(collect);
例.获取一个所有书名的set集合
Set<Book> collect1 = authors.stream()
.flatMap(author -> author.getBooks().stream())
.collect(Collectors.toSet());
System.out.println(collect1);
例.获取一个map集合,map的key为作者名,value为List
Map<String, List<Book>> collect2 = authors.stream().distinct()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
System.out.println(collect2);
anyMatch
只要有一个满足条件,就返回true
allMatch
如果都符合,结果为true
boolean b = authors.stream()
.allMatch(author -> author.getAge() > 18);
System.out.println(b);
noneMatch
可以判断流中元素是否都不符合匹配条件,如果都不符合结果为true
findAny
获取流中的任意一个,该方法没有办法保证获取的一定是流中的第一个元素。
findFirst
获取流中的第一个元素
reduce
归并
对流中的数据按照你制定的计算方法计算出一个结果
reduce的作用就是把stream()中的元素组合起来,我们可以传入一个初始值,他会按着我们的计算方式依次拿流中的元素和初始化值进行计算,计算结果在和后面的元素计算。
//求所有作者的年龄之和
Integer reduce = authors.stream()
.map(author -> author.getAge())
.reduce(0, (integer, integer2) -> integer + integer2);
System.out.println(reduce);
//求所有作者中,年龄最小的值
Integer reduce1 = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MAX_VALUE, (integer, integer2) -> integer > integer2 ? integer2 : integer);
System.out.println(reduce1);