当前位置: 首页 > web >正文

16-集合的Stream编程

一 Stream编程简介

Stream编程(也叫集合的流式编程),是JDK1.8引入的新特性,也是JDK1.8新特性中最值得学习的两种新特性之一。(另外一个是 lambda表达式)。

Stream是对集合操作的增强,Stream不是一种数据结构,不负责数据的存储。流更像是一个迭代器,可以单向的遍历一个集合中的每一个元素,并且不可循环。

为什么要引入这个特性呢?

有些时候,对集合中的元素进行操作的时候,需要使用到其他操作的结果。在这个过程中,集合的流式 编程可以大幅度的简化代码的数量。将数据源中的数据,读取到一个流中,可以对这个流中的数据进行 操作(删除、过滤、映射…)。每次的操作结果也是一个流对象,可以对这个流再进行其他的操作。

二 Stream编程步骤

通常情况下,对集合中的数据使用流式编程,需要经过以下三步。

  1. 获取数据源,将数据源中的数据读取到流中。
  2. 对流中的数据进行各种各样的处理。
  3. 对流中的数据进行整合处理。

在上述三个过程中,

过程2中,有若干方法,可以对流中的数据进行各种各样的操作,并且返回流对象 本身,这样的操作,被称为 – 中间操作。

过程3中,有若干方法,可以对流中的数据进行各种处理,并关闭流,这样的操作,被称为 – 最终操作。

在中间操作和最终操作中,基本上所有的方法参数都是函数式接口,可以使用lambda表达式来实现。 使用集合的流式编程,来简化代码量,是需要对 lambda 表达式做到熟练掌握。

三 数据源的获取

数据源,顾名思义,既是流中的数据的来源。 集合的流式编程的第一步,将数据源中的数据读取到流中,进行处理。这个过程,其实是将一个容器中的数据,读取到一个流中。因此无论什么容器作为数据源,读取到流中的方法返回值一定是一个Stream。

// 1、通过Collection接口中的stream()方法获取数据源为Collection的流
Stream<Integer> stream = list.stream();
// 2、通过Collection接口的parallelStream()方法获取数据源为Collection的流
Stream<Integer> stream = list.parallelStream();
// 3、通过Arrays工具类中的stream()方法获取数据源为数组的流
IntStream stream = Arrays.stream(array);

stream() 和 parallelStream的比较:

  • 他们都是Collection集合获取数据源的方法,
  • 不同点在于stream()方法获取的数据源是串行的,parallelStream()获取的数据源是并行的。 parallelStream()内部集成了多个线程对流中的数据进行操作,效率更高。

注意:

将数据读取到流中进行处理的时候,与数据源中的数据没有关系。也就是说,中间操作对流中的数据进行处理、过滤、映射、排序… ,此时是不会影响数据源中的数据的

四 最终操作方法

将流中的数据整合到一起,可以存入一个集合,也可以直接对流中的数据进行遍历、数据统计… ,通过最终操作,需要掌握如何从流中提取出来我们想要的信息。

注意事项:

最终操作,之所以叫最终操作,是因为,在最终操作执行结束后,会关闭这个流,流中的所有数据都会销毁。如果使用一个已经关闭了的流,会出现异常。

4.1 collect方法

将流中的数据收集到一起,对这些数据进行一些处理。最常见的处理,就是将流中的数据存入一个集 合。 collect方法的参数,是一个Collector接口,而且这个接口并不是一个函数式接口。实现这个接口, 可以自定义收集的规则。但是,绝大部分情况下,不需要自定义,直接使用Collectors工具类提供的方法即可

// 1.1、转成  List
List<Integer> result1 = list.stream().collect(Collectors.toList());
System.out.println(result1);// 1.2、转成  Set
Set<Integer> result2 = list.stream().collect(Collectors.toSet());
System.out.println(result2);// 1.3、转成  Map,提供两个函数式接口的实现,分别实现键的生成规则和值的生成规则
Map<Integer, Integer> result3 = list.stream().collect(Collectors.toMap(ele ->ele / 10, ele -> ele));
System.out.println(result3);

toMap方法源码解析

public static <T, K, U>  Collector<T, ?, Map<K,U>>   toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {return new CollectorImpl<>(HashMap::new,uniqKeysMapAccumulator(keyMapper, valueMapper),uniqKeysMapMerger(),CH_ID);
}参数源码解析:
1. 查看Function源码,@FunctionalInterfacepublic interface Function<T, R> {R apply(T t);//...省略...}结论: 其实就是使用lambda表达式,实现apply方法的匿名写法。使用时如下: x 和 y都是指向的元素本身,toMap( x-> {...}, y->{...})   

4.2 reduce方法

将流中的数据按照一定的规则聚合起来。

// 将流的元素,逐一带入到这个方法中,进行运算
// 最终的运算结果,得到的其实是一个  Optional 类型,需要使用get() 获取到里面的数据
int result4 = list.stream().reduce((e1, e2) -> e1 + e2).get();
System.out.println(result4);

4.3 count

统计流中的元素数量。

long result5 = list.stream().count();
System.out.println(result5);

4.4 forEach

迭代、遍历流中的数据。

list.stream().forEach(System.out::println);

4.5 max & min

获取流中的最大的元素、最小的元素。

// 获取最大值
Integer result6 = list.stream().max(Integer::compareTo).get();
System.out.println("max is : " + result6);
// 获取最小值
Integer result7 = list.stream().min(Integer::compareTo).get();
System.out.println("min is : " + result7);

4.6 Matching

  • allMatch: 只有当流中所有的元素,都匹配指定的规则,才会返回 true
  • anyMatch: 只要流中有任意的数据,满足指定的规则,都会返回 true
  • noneMatch: 只有当流中的所有的元素,都不满足指定的规则,才会返回true
// 判断流中是否所有的元素都大于  50
boolean result8 = list.stream().allMatch(ele -> ele > 50);
System.out.println(result8);// 判断流中是否有大于  50 的数据
boolean result9 = list.stream().anyMatch(ele -> ele > 50);
System.out.println(result9);// 判断流中是否没有奇数
boolean result10 = list.stream().noneMatch(ele -> ele % 2 != 0);
System.out.println(result10);

4.7 find

  • findFirst: 从流中获取一个元素(一般情况下,是获取的开头的元素)
  • findAny: 从流中获取一个元素(一般情况下,是获取的开头的元素)

这两个方法,绝大部分情况下,是完全相同的,但是在多线程的环境下, findAny和findFirst返回的结果可能不一样。

Integer result11 = list.parallelStream().findFirst().get();
System.out.println(result11);Integer result12 = list.parallelStream().findAny().get();
System.out.println(result12);

4.8 最终操作的注意事项

最终操作,会关闭流。如果一个流被关闭了,再去使用这个流,就出出现异常。

// 9、最终操作错误示范
Stream<Integer> stream = list.stream();
long count = stream.count();
stream.forEach(System.out::println);
Exception in thread "main" java.lang.IllegalStateException: stream has already
been operated upon or closed
at
java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java
:279)
at
java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.qf.cstream.FinalOperationDemo.main(FinalOperationDemo.java:78)

五 中间操作方法

5.1 filter

5.2 distinct

5.3 sorted

5.4 limit & skip

5.5 map & flatMap

5.6 mapToInt

六 工具类

Collectors是一个工具类,里面封装了很多方法,可以很方便的获取到一个 Collector 接口的实现类对 象,从而可以使用 collect() 方法,对流中的数据,进行各种各样的处理、整合。

Collectors.toList() : 将流中的数据,聚合到一个  List 集合中 
Collectors.toSet()  : 将流中的数据,聚合到一个  Set 集合中   
Collectors.toMap()  : 将流中的数据,聚合到一个  Map 集合中
maxBy()             : 按照指定的规则,找到流中最大的元素,等同于  max
minBy()             : 按照指定的规则,找到流中最小的元素,等同于  minjoining()           : 将流中的数据拼接成一个字符串,注意:只能操作流中是String的数据summingInt()        : 将流中的数据,映射成  int 类型的数据,并求和
averagingInt()      : 将流中的数据,映射成  int 类型的数据,并求平均值
summarizingInt()    : 将流中的数据,映射成  int 类型的数据,并获取描述信息
// maxBy: 按照指定的规则,找到流中最大的元素,等同于  max
Student max = list.stream().collect(Collectors.maxBy((s1, s2) -> s1.getScore() -s2.getScore())).get();
System.out.println(max);// minBy: 按照指定的规则,找到流中最小的元素,等同于  min
Student min = list.stream()
.collect(Collectors.minBy((s1, s2) -> s1.getScore() -s2.getScore()))
.get();
System.out.println(min);// 将流中的数据,拼接起来
String s1 = list.stream().map(Student::getName).collect(Collectors.joining());
System.out.println(s1);// 将流中的数据,拼接起来,以指定的分隔符进行分隔
String s2 = list.stream().map(Student::getName).collect(Collectors.joining(", "));
System.out.println(s2);// 将流中的数据,拼接起来,以指定的分隔符进行分隔,并添加前缀和尾缀
String s3 = list.stream().map(Student::getName).collect(Collectors.joining(", ", "{", "}"));
System.out.println(s3);// 将流中的数据,映射成  int 类型的数据,并求和
int sum = list.stream().collect(Collectors.summingInt(Student::getScore));
System.out.println(sum);// 将流中的数据,映射成  int 类型的数据,并求平均值
double average = list.stream().collect(Collectors.averagingInt(Student::getScore));
System.out.println(average);// 将流中的数据,映射成 int 类型的数据,并获取描述信息
IntSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingInt(Student::getScore));System.out.println(summaryStatistics);
System.out.println(summaryStatistics.getCount());
System.out.println(summaryStatistics.getSum());
System.out.println(summaryStatistics.getMax());
System.out.println(summaryStatistics.getMin());
System.out.println(summaryStatistics.getAverage());

七 练习题

需求 : 一个集合中存储了了若干个Student对象 , 要求查询出以下结果 :

  1. 所有及格的学生信息
  2. 所有及格的学生姓名
  3. 所有学生的平均成绩
  4. 班级的前3名(按照成绩)
  5. 班级的3-10名(按照成绩)
  6. 所有不不及格的学生平均成绩
  7. 将及格的学生 , 按照成绩降序输出所有信息
  8. 班级学生的总分
public static void main(String[] args) {List<Student> list = new ArrayList<Student>();Student s1 = new Student("小王",'M',99.0);Student s2 = new Student("小李",'M',50.0);Student s3 = new Student("小胡",'M',80.0);Student s4 = new Student("小周",'M',75.0);Student s5 = new Student("小唐",'M',80.0);Student s6 = new Student("小雷",'M',85.0);Student s7 = new Student("小陈",'M',45.0);Student s8 = new Student("小赵",'M',65.0);Student s9 = new Student("小钱",'M',55.0);Student s10 = new Student("小明",'M',100.0);list.addAll(Arrays.asList(s1,s2,s3,s4,s5,s6,s7,s8,s9,s10));Stream<Student> st = list.stream();//需求:1. 所有及格的学生信息List<Student> students = st.filter(e -> e.getScore() >= 60).collect(Collectors.toList());System.out.println(students);//需求:2. 所有及格的学生姓名List<String> students = st.filter(e -> e.getScore() >= 60).map(Student::getNeme).collect(Collectors.toList());System.out.println(students);//需求:所有学生的平均成绩double average = st.mapToDouble((x) -> x.getScore()).summaryStatistics().getAverage();System.out.println(average);//74.4//需求:班级的前3名(按照成绩)List<String> list1 = st.sorted(Comparator.comparingDouble(Student::getScore).reversed()).limit(3).map(Student::getNeme).toList();System.out.println(list1);//需求:班级的3-10名(按照成绩)List<String> list1 = st.sorted(Comparator.comparingDouble(Student::getScore).reversed()).skip(2).limit(8).map(Student::getNeme).toList();System.out.println(list1);//需求:所有不不及格的学生平均成绩Double v = st.filter(x -> x.getScore() < 60).collect(Collectors.averagingDouble(x -> x.getScore()));System.out.println(v);//50.0//需求:将及格的学生 , 按照成绩降序输出所有信息List<Student> list1 = st.filter(x -> x.getScore() >= 60).sorted(Comparator.comparingDouble(Student::getScore).reversed()).toList();System.out.println(list1);//需求:班级学生的总分Double sumScore = st.collect(Collectors.summingDouble((x) -> x.getScore()));System.out.println(sumScore);//744.0}
http://www.xdnf.cn/news/18065.html

相关文章:

  • 宋红康 JVM 笔记 Day03|内存结构概述、类加载器与类的加载过程、类加载器分类
  • 深入解析 @nestjs/typeorm的 forRoot 与 forFeature
  • C++面试题及详细答案100道( 31-40 )
  • 算法题Day2
  • Python 类元编程(元类的特殊方法 __prepare__)
  • MixOne:Electron Remote模块的现代化继任者
  • 【低成本扩容】动态扩容实战指南
  • 选择式与生成式超启发算法总结
  • 《设计模式》代理模式
  • 基于Python的电影评论数据分析系统 Python+Django+Vue.js
  • 【运维心得】三步10分钟拆装笔记本键盘
  • Langfuse2.60.3:独立数据库+docker部署及环境变量详细说明
  • 数据清洗处理
  • 【数据结构】深入理解单链表与通讯录项目实现
  • 【洛谷刷题】用C语言和C++做一些入门题,练习洛谷IDE模式:分支机构(一)
  • 典型 RAG实现:NFRA智能问答系统实战的总结与反思
  • 数据结构:迭代方法(Iteration)实现树的遍历
  • ubuntu更新chrome版本
  • 平滑方法(smoothing)
  • 零知开源——基于STM32F407VET6的TCS230颜色识别器设计与实现
  • 两个简单的设计模式的例子
  • 【轨物方案】预防性运维:轨物科技用AI+机器人重塑光伏电站价值链
  • JavaScript 核心语法与实战笔记:从基础到面试高频题
  • NLP:Transformer模型构建
  • 驱动开发系列63 - 配置 nvidia 的 open-gpu-kernel-modules 调试环境
  • ES操作手册
  • 在本地部署Qwen大语言模型全过程总结
  • Linux -- 线程概念与控制
  • 【DDIA】第三部分:衍生数据
  • AI优质信息源汇总:含X账号,Newsletter,播客,App