一、什么是Stream流
Stream
将要处理的元素集合看作一种流,在流的过程中,借助Stream API
对流中的元素进行操作,比如:筛选、排序、聚合等。
Stream有三大特性:
1.stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
2.stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
3.stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
Stream
可以由数组或集合创建,对流的操作分为两种:
1.中间操作,每次返回一个新的流,可以有多个。
2.终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
二、Stream创建
1.Stream创建的两种方式:
1.1.串行流
串行流上的操作是在一个线程中依次完成
1.2.并行流
并行流则是在多个线程上同时执行
所以并行流的效率比串行流高。
List<String> list = Arrays.asList("code", "wang", "codeWang");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
2.Stream的三个静态方法:of()、iterate()、generate()
// of()方法 返回元素的顺序流
Stream.of(1, 2, 3).forEach(System.out::println);
// iterate()方法,第一个参数是初始值,第二个参数是一个方法,对每个元素进行操作得到新值
// 这个方法获取的是无限流,需要借助limit() 方法来截取
Stream.iterate(1, (x) -> x + 4).limit(2).forEach(System.out::println);
// generate() 方法 返回无限无序流
// Stream.generate(new Random()::nextBoolean) 随机Boolean类型
// Stream.generate(new Random()::nextInt) 随机Int类型
Stream.generate(Math::random).limit(2).forEach(System.out::println);
of方法结果: 1 2 3
iterate方法结果: 1 5
generate方法结果: 0.792347429124145 0.19113911202391165
三、Stream操作
3.1、筛选 / 去重 / 匹配(filter() / distinct() / find() / match())
List<Integer> list = Arrays.asList(1, 2, 3, 4, 2, 1, 6, 7, 8, 9, 10);
// 去重
list.stream().distinct().forEach(System.out::print);
// 满足条件 匹配第一个
Optional<Integer> findFirst = list.stream().filter(x -> x > 3).findFirst();
// 匹配任意(适用于并行流)
Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
// 是否包含符合特定条件的元素
boolean anyMatch = list.stream().anyMatch(x -> x < 6);
System.out.println("匹配第一个:" + findFirst.get());
System.out.println("匹配任意:" + findAny.get());
System.out.println("是否包含符合条件的元素:" + anyMatch);
结果输出
filter筛选出集合中大于3的元素: 4 6 7 8 9 10 6
distinct去重: 1 2 3 4 6 7 8 9 10
匹配第一个:4
匹配任意:7
是否包含符合条件的元素:true
3.2、聚合(max() / min() / count())
3.2.1、String
集合中最长的元素。
List<String> stringList = Arrays.asList("code", "wang", "codeWang", "coderWang");
Optional<String> stringMax = stringList.stream().max(Comparator.comparing(String::length));
System.out.println("String集合中最长的元素:"+stringMax.get());
String集合中最长的元素:coderWang
3.2.2、Integer
集合中的最大值。
List<Integer> integerList = Arrays.asList(5, 2, 9, 4, 16, 6);
// 自然排序
Optional<Integer> integerMax = integerList.stream().max(Integer::compareTo);
System.out.println("Integer集合中的最大值:" + integerMax.get());
Integer集合中的最大值:16
3.2.3、 Integer
集合中的最小值。
List<Integer> integerList = Arrays.asList(5, 2, 9, 4, 16, 6);
// 自定义排序
Optional<Integer> integerMin = integerList.stream().min(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
System.out.println("Integer集合中的最小值:" + integerMin.get());
Integer集合中的最小值:2
3.2.4、获取学生中成绩最高的学生
(本文有关实体类操作都是基于此类)
// 姓名
private String name;
// 年龄
private Integer age;
// 分数
private Double score;
// 学科
private String subject;
public List<UserEntity> createList() {
List<UserEntity> userList = new ArrayList<>();
userList.add(new UserEntity("姬如雪", 15, 96.5, "语文"));
userList.add(new UserEntity("女帝", 18, 96.0, "语文"));
userList.add(new UserEntity("李星云", 16, 85.5, "语文"));
userList.add(new UserEntity("姬如雪", 15, 92.5, "数学"));
userList.add(new UserEntity("女帝", 18, 95.0, "数学"));
userList.add(new UserEntity("李星云", 16, 90.5, "数学"));
return userList;
}
public UserEntity() {
}
public UserEntity(String name, Integer age, Double score, String subject) {
this.name = name;
this.age = age;
this.score = score;
this.subject = subject;
}
UserEntity userEntity=new UserEntity();
List<UserEntity> userList = userEntity.createList();
Optional<UserEntity> max = userList.stream().max(Comparator.comparingDouble(UserEntity::getScore));
System.out.println("对象集合中分数最高的人:"+max.get().getName());
对象集合中分数最高的人:姬如雪
3.2.5、计算集合中大于6的元素的个数。
List<Integer> integerList = Arrays.asList(5, 2, 9, 4, 16, 6);
long count = integerList.stream().filter(x -> x > 3).count();
System.out.println("list中大于3的元素个数:" + count);
list中大于3的元素个数:5
3.3、收集(collect) 归集(toList/toSet/toMap)
collect
收集,可以说是内容最繁多、功能最丰富的部分了。可以理解为把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。
3.3.1、toList
List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 8, 9, 6, 5);
List<Integer> listNew =
list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
System.out.println("初始化集合:" + list);
System.out.println("过滤生成新的集合:" + listNew);
初始化集合:[1, 6, 3, 4, 6, 8, 9, 6, 5]
过滤生成新的集合:[6, 4, 6, 8, 6]
3.3.2、toSet
List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 8, 9, 6, 5);
Set<Integer> set =
list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
System.out.println("初始化集合:" + list);
System.out.println("过滤生成Set集合:" + set);
初始化集合:[1, 6, 3, 4, 6, 8, 9, 6, 5]
过滤生成Set集合:[4, 6, 8]
3.3.3、toMap
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
Map<String, UserEntity> userMap = 、userList.stream().filter(p -> p.getScore() > 95)
.collect(Collectors.toMap(UserEntity::getName, p -> p));
System.out.println(userMap);
{女帝=com.enter.entity.UserEntity@4f3f5b24, 姬如雪=com.enter.entity.UserEntity@15aeb7ab}
3.4、映射(map/flatMap)
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
3.4.1、map
英文字符串数组的元素全部改为小写。数组每个元素乘上3。
String[] strArr = { "CODE", "Wang"};
List<String> strList = Arrays.stream(strArr).map(String::toLowerCase).collect(Collectors.toList());
System.out.println("元素小写:"+strList);
List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
List<Integer> intListNew = intList.stream().map(x -> x * 3).collect(Collectors.toList());
System.out.println("每个元素*3:"+intListNew);
元素小写:[code, wang]
每个元素*3:[3, 9, 15, 21, 27, 33]
3.4.2、 flatMap
将两个字符数组合并成一个新的字符数组。
List<String> list = Arrays.asList("c-o-d-e", "w-a-n-g");
List<String> listNew = list.stream().flatMap(s -> {
// 将每个元素转换成一个stream
String[] split = s.split("-");
Stream<String> s2 = Arrays.stream(split);
return s2;
}).collect(Collectors.toList());
System.out.println("处理前的集合:" + list);
System.out.println("处理后的集合:" + listNew);
处理前的集合:[c-o-d-e, w-a-n-g]
处理后的集合:[c, o, d, e, w, a, n, g]
3.5、归约(reduce / reducing)
3.5.1、归约(reduce)
也称缩减,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
// 求和方式1
Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
// 求和方式2
Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
// 求和方式3
Integer sum3 = list.stream().reduce(0, Integer::sum);
// 求乘积
Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
// 求最大值方式1
Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
// 求最大值写法2
Integer max2 = list.stream().reduce(1, Integer::max);
System.out.println("list求和方式一:" + sum.get() + ",方式二:" + sum2.get() + ",方式三:" + sum3);
System.out.println("list求积:" + product.get());
System.out.println("list求最大值方式一:" + max.get() + ",求最大值方式二:" + max2);
list求和方式一:29,方式二:29,方式三:29
list求积:2112
list求最大值方式一:11,求最大值方式二:11
3.5.2、归约(reducing)
Collectors
类提供的reducing
方法,相比于stream
本身的reduce
方法,增加了对自定义归约 的支持。
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
// 每个学员各科成绩减去5之后的成绩之合
Double sum = userList.stream().collect(Collectors.reducing(0.0, UserEntity::getScore, (i, j) -> (i + j - 5)));
System.out.println("员工减去国定值总和:" + sum);
// stream的reduce
Optional<Double> sum2 = userList.stream().map(UserEntity::getScore).reduce(Double::sum);
System.out.println("所有成绩总和:" + sum2.get());
员工减去国定值总和:526.0
所有成绩总和:556.0
3.6、统计
Collectors
提供了一系列用于数据统计的静态方法:
- 计数:
count
- 平均值:
averagingInt
、averagingLong
、averagingDouble
- 最值:
maxBy
、minBy
- 求和:
summingInt
、summingLong
、summingDouble
统计以上所有:summarizingInt、summarizingLong、summarizingDouble
3.6.1、计数
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
// 总数
Long count = userList.stream().collect(Collectors.counting());
System.out.println("List总数:" + count);
List总数:6
3.6.2、平均值
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
// 平均成绩
Double average = userList.stream().collect(Collectors.averagingDouble(UserEntity::getScore));
System.out.println("学生平均成绩:" + average);
学生平均成绩:92.66666666666667
3.6.3、最值
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
// 最高成绩
Optional<Double> max = userList.stream().map(UserEntity::getScore).collect(Collectors.maxBy(Double::compare));
System.out.println("最高成绩:" + max.get());
最高成绩:96.5
3.6.4、求和
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
// 总和
Double sum = userList.stream().collect(Collectors.summingDouble(UserEntity::getScore));
System.out.println("成绩总和:" + sum);
成绩总和:556.0
3.6.5、统计以上所有
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
// 一次性统计所有信息
DoubleSummaryStatistics collect = userList.stream().collect(Collectors.summarizingDouble(UserEntity::getScore));
System.out.println("成绩所有统计:" + collect);
成绩所有统计:DoubleSummaryStatistics{count=6, sum=556.000000, min=85.500000, average=92.666667, max=96.500000}
3.7、分组(partitioningBy/groupingBy)
分区:将stream按条件分为两个Map,比如员工按成绩是否高于95分为两部分。
分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
// 按成绩是否高于95分组
Map<Boolean, List<UserEntity>> part = userList.stream().collect(Collectors.partitioningBy(x -> x.getScore() > 95));
// 按性别分组
Map<String, List<UserEntity>> group = userList.stream().collect(Collectors.groupingBy(UserEntity::getSex));
// 先按性别分组,再按学科分组
Map<String, Map<String, List<UserEntity>>> group2 =
userList.stream().collect(Collectors.groupingBy(UserEntity::getSex, Collectors.groupingBy(UserEntity::getSubject)));
System.out.println("按成绩是否大于95分组情况:" + part);
System.out.println("按性别分组情况:" + group);
System.out.println("按性别、学科:" + group2);
按成绩是否大于95分组情况:{false=[com.enter.entity.UserEntity@1d81eb93, com.enter.entity.UserEntity@7291c18f, com.enter.entity.UserEntity@34a245ab, com.enter.entity.UserEntity@7cc355be], true=[com.enter.entity.UserEntity@6e8cf4c6, com.enter.entity.UserEntity@12edcd21]}
按性别分组情况:{女=[com.enter.entity.UserEntity@6e8cf4c6, com.enter.entity.UserEntity@12edcd21, com.enter.entity.UserEntity@7291c18f, com.enter.entity.UserEntity@34a245ab], 男=[com.enter.entity.UserEntity@1d81eb93, com.enter.entity.UserEntity@7cc355be]}
按性别、学科:{女={数学=[com.enter.entity.UserEntity@7291c18f, com.enter.entity.UserEntity@34a245ab], 语文=[com.enter.entity.UserEntity@6e8cf4c6, com.enter.entity.UserEntity@12edcd21]}, 男={数学=[com.enter.entity.UserEntity@7cc355be], 语文=[com.enter.entity.UserEntity@1d81eb93]}}
3.8、接合(joining)
joining
可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
String names = userList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
System.out.println("所有学员的姓名:" + names);
所有学员的姓名:姬如雪,女帝,李星云,姬如雪,女帝,李星云
3.9、排序(sorted)
sorted,中间操作。有两种排序:
sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):Comparator排序器自定义排序
UserEntity userEntity = new UserEntity();
List<UserEntity> userList = userEntity.createList();
// 按成绩升序排序(自然排序)
List<String> newList = userList.stream().sorted(Comparator.comparing(UserEntity::getScore)).map(UserEntity::getName)
.collect(Collectors.toList());
// 按成绩倒序排序
List<String> newList2 = userList.stream().sorted(Comparator.comparing(UserEntity::getScore).reversed())
.map(UserEntity::getName).collect(Collectors.toList());
// 先按成绩再按年龄升序排序
List<String> newList3 = userList.stream()
.sorted(Comparator.comparing(UserEntity::getScore).thenComparing(UserEntity::getAge)).map(UserEntity::getName)
.collect(Collectors.toList());
// 先按成绩再按年龄自定义排序(降序)
List<String> newList4 = userList.stream().sorted((p1, p2) -> {
if (p1.getScore() == p2.getScore()) {
return p2.getAge() - p1.getAge();
} else {
return (int) (p2.getScore() - p1.getScore());
}
}).map(UserEntity::getName).collect(Collectors.toList());
System.out.println("按成绩升序排序:" + newList);
System.out.println("按成绩降序排序:" + newList2);
System.out.println("先按成绩再按年龄升序排序:" + newList3);
System.out.println("先按成绩再按年龄自定义降序排序:" + newList4);
按成绩升序排序:[李星云, 李星云, 姬如雪, 女帝, 女帝, 姬如雪]
按成绩降序排序:[姬如雪, 女帝, 女帝, 姬如雪, 李星云, 李星云]
先按成绩再按年龄升序排序:[李星云, 李星云, 姬如雪, 女帝, 女帝, 姬如雪]
先按成绩再按年龄自定义降序排序:[姬如雪, 女帝, 女帝, 姬如雪, 李星云, 李星云]
4、 合并、去重、限制、跳过
String[] arr1 = {"c", "o", "d", "e"};
String[] arr2 = {"w", "a", "n", "g", "e"};
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
// 使用concat:合并两个流 distinct:去重
List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
// 使用limit:限制从流中获得前n个数据
List<Integer> limitList = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
// 使用skip:跳过前n个数据
List<Integer> skipList = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
System.out.println("流合并:" + newList);
System.out.println("限制 :" + limitList);
System.out.println("跳过 :" + skipList);
流合并:[c, o, d, e, w, a, n, g]
限制 :[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
跳过 :[3, 5, 7, 9, 11]