Stream流
一、Stream的中间操作
distinct():去重
java User.Stream().distinct()
filter:过滤
java students.stream().filter(student -> "清华大学".equals(student.getSchool())).collect(Collectors.toList());
limit:limit操作也类似于SQL语句中的
LIMIT
关键字,不过相对功能较弱,limit返回包含前n个元素的流,当集合大小小于n时,则返回实际长度。java students.stream().filter(student -> "计算机".equals(student.getMajor())).limit(2).collect(Collectors.toList());
.sorted() :排序```java // 状态排序 orderListDTOS.stream().sorted(Comparator.comparing(OrderListDTO::getOrderStatusStr)
// 评价时间 为null靠前
.thenComparing(OrderListDTO::getEvaluateTime, Comparator.nullsFirst(String::compareTo).reversed())
// 下单时间 倒序
.thenComparing(OrderListDTO::getOrderTime, Comparator.nullsFirst(String::compareTo)).reversed())
// 赋值给新集合 进行保存
.collect(Collectors.toList()).forEach(order -> {
OrderListDTO orderListDTO = new OrderListDTO();
BeanUtils.copyProperties(order, orderListDTO);
orders.add(orderListDTO);
});
```
skip:是跳过前n个元素
java students.stream().filter(student -> "计算机科学".equals(student.getMajor())).skip(2)
of:实现判断```java String status = pouch.getStatus(); if (Stream.of(
"Finalized",
"Ready",
"Checkout",
"Confirmed",
"Book",
"Started",
"Inital",
"Close")
.anyMatch(status::equals)) {// anyMatch:检测是否存在一个或多个满足指定的参数行为
// 逻辑代码块 } ```
二、终端操作:
2.1 查找:
allMatch:allMatch用于检测是否全部都满足指定的参数行为,如果全部满足则返回true,如下:学生年龄是否大于等于18。
java boolean isAdult = students.stream().allMatch(student -> student.getAge() >= 18);
anyMatch:anyMatch则是检测是否存在一个或多个满足指定的参数行为,如果满足则返回true,如下:所有学生中是否有清华大学的学生。
java boolean hasWhu = students.stream().anyMatch(student -> "清华大学".equals(student.getSchool()));
noneMathch:noneMatch用于检测是否不存在满足指定行为的元素,如果不存在则返回true,如下:检测是否不存在专业为计算机科学的学生。
java boolean noneCs = students.stream().noneMatch(student -> "计算机科学".equals(student.getMajor()));
findFirst:用于返回满足条件的第一个元素
java students.stream().filter(student -> "计算机科学".equals(student.getMajor())).findFirst();
findAny:相对于findFirst的区别在于,findAny不一定返回第一个,而是返回任意一个
java students.stream().filter(student -> "计算机".equals(student.getMajor())).findAny();
实际上对于顺序流式处理而言,findFirst和findAny返回的结果是一样的,至于为什么会这样设计,是因为在下一篇我们介绍的并行流式处理,当我们启用并行流式处理的时候,查找第一个元素往往会有很多限制,如果不是特别需求,在并行流式处理中使用findAny的性能要比findFirst好。
2.2 归约:
我们大部分都是通过
collect(Collectors.toList())
对数据封装返回,如我的目标不是返回一个新的集合,而是希望对经过参数化操作后的集合进行进一步的运算,那么我们可用对集合实施归约操作。java8的流式处理提供了reduce
方法来达到这一目的。
前面我们通过mapToInt将Stream<Student>
映射成为IntStream
,并通过IntStream
的sum方法求得所有学生的年龄之和,实际上我们通过归约操作,也可以达到这一目的,实现如下:
// 前面例子中的方法
int totalAge = students.stream()
.filter(student -> "计算机科学".equals(student.getMajor()))
.mapToInt(Student::getAge).sum();
// 归约操作
int totalAge = students.stream()
.filter(student -> "计算机科学".equals(student.getMajor()))
.map(Student::getAge)
.reduce(0, (a, b) -> a + b);
// 进一步简化
int totalAge2 = students.stream()
.filter(student -> "计算机科学".equals(student.getMajor()))
.map(Student::getAge)
.reduce(0, Integer::sum);
// 采用无初始值的重载版本,需要注意返回Optional
Optional<Integer> totalAge = students.stream()
.filter(student -> "计算机科学".equals(student.getMajor()))
.map(Student::getAge)
.reduce(Integer::sum); // 去掉初始值
2.3 收集:
前面利用collect(Collectors.toList())
是一个简单的收集操作,是对处理结果的封装,对应的还有toSet
、toMap
,以满足我们对于结果组织的需求。这些方法均来自于java.util.stream.Collectors
,我们可以称之为收集器。
2.3.1 规约:
收集器也提供了相应的归约操作,但是与reduce在内部实现上是有区别的,收集器更加适用于可变容器上的归约操作,这些收集器广义上均基于
Collectors.reducing()
实现。
(1). 求学生总数:.count()、Collectors.counting()
long count = students.stream().collect(Collectors.counting());
// 进一步简化
long count = students.stream().count();
(2). 求最大值和最小值:Collectors.maxBy、Collectors.minBy
// 求最大年龄
Optional<Student> olderStudent = students.stream().collect(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge()));
// 进一步简化
Optional<Student> olderStudent2 = students.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)));
// 求最小年龄
Optional<Student> olderStudent3 = students.stream().collect(Collectors.minBy(Comparator.comparing(Student::getAge)));
(3). 求总和:Collectors.summingInt
int totalAge4 = students.stream().collect(Collectors.summingInt(Student::getAge));
// 对应的还有summingLong、summingDouble。
求平均值:**Collectors.averagingInt**
double avgAge = students.stream().collect(Collectors.averagingInt(Student::getAge));
// 对应的还有averagingLong、averagingDouble。
一次性得到元素个数、总和、均值、最大值、最小值:
IntSummaryStatistics statistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));
// 输出:
IntSummaryStatistics{count=10, sum=220, min=20, average=22.000000, max=24}
对应的还有summarizingLong
、summarizingDouble
…
(4). 字符串拼接 : Collectors.joining(“”)
String names = students.stream().map(Student::getName).collect(Collectors.joining());
// 输出:孔明伯约玄德云长翼德元直奉孝仲谋鲁肃丁奉
String names = students.stream().map(Student::getName).collect(Collectors.joining(", "));
// 输出:孔明, 伯约, 玄德, 云长, 翼德, 元直, 奉孝, 仲谋, 鲁肃, 丁奉
2.3.2 分组:
在数据库操作中,我们可以通过GROUP BY
关键字对查询到的数据进行分组,java8的流式处理也为我们提供了这样的功能Collectors.groupingBy
来操作集合:
students.stream().collect(
Collectors.groupingBy(Student::getSchool, // 一级分组,按学校
Collectors.groupingBy(Student::getMajor))); // 二级分组,按专业
实际上在groupingBy
的第二个参数不是只能传递groupingBy,还可以传递任意Collector
类型,比如我们可以传递一个Collector.counting
,用以统计每个组的个数:
Map<String, Long> groups = students.stream().collect(Collectors.groupingBy(Student::getSchool, Collectors.counting()));
如果我们不添加第二个参数,则编译器会默认帮我们添加一个Collectors.toList()
。
2.3.3 分区:
分区可以看做是分组的一种特殊情况,在分区中key只有两种情况:true或false,目的是将待分区集合按照条件一分为二,处理利用collectors.partitioningBy()
方法实现分区,该方法接收一个谓词,例如我们希望将学生分为武大学生和非武大学生,那么可以实现如下:
Map<Boolean, List<Student>> partition = students.stream().collect(Collectors.partitioningBy(student -> "武汉大学".equals(student.getSchool())));
例如:比如将数组分为奇数和偶数。
三、parallelStream(并行流,相比于Stream更快但是涉及到线程安全)
list.parallelStream().map(e -> e + 3).collect(Collectors.toList());
parallelStream在处理大的数据量的集合时使用,但是线程不安全,可以先map,再.collect(Collectors.toList(),.collect(Collectors.toList()可以封装一个List集合返回,还可以保证线程安全。
———-错误记录:———-
1、Collectors.toMap value为null报错
Map<String, Integer> map3 = new HashMap<>(0);
map3 = list3.stream().collect(Collectors.toMap(
list3 -> Optional.ofNullable(list3).map(SpGoodsIdAndCount::getGoodsId).orElse("")
, list3 -> Optional.ofNullable(list3).map(SpGoodsIdAndCount::getCount).orElse(0)
, (key1, key2) -> key2));
解析: Collectors.toMap():list集合转换为map,通过Optional判断当key和value为null时赋予默认值。 (key1, key2) -> key2):当key重复时,选择key2作为新的key值。