草庐IT

【java8】stream学习笔记

无明梦终醒 2023-04-16 原文

目录

一、什么是Stream

Stream是java8的新增特性,表示数据流。

二、Stream的特点

  1. 不存储数据:流是基于数据源的对象,本身不存储数据。
  2. 函数式编程:流的操作不会修改数据源。
  3. 延迟操作:流的很多操作如filtermap是延迟执行的,只有到了终点操作才会将操作顺序执行。
  4. 可以解绑:对于无线数量的流,有些操作(如limit(n)findFirst())可以实现“短路操作”,访问到有限的元素后就可以返回。
  5. 纯消费:流只能被消费一次。如果使用完了一次流,仍然用这个流对象来操作时,会报异常。

串并行

所有的流操作都可以串行执行或者并行执行。除非显示的创建并行流,否则默认创建串行流。

串行:Collection.stream()IntStream.range(int, int)

并行:Collection.parallelStream()、

通过parallel()方法可以将串行流转换为并行流,sequential()方法将流转换为串行流

干扰数据源

流可以从非线程安全的集合中创建。当流执行时,非并发的数据源不应该改变,否则会抛出java.util.ConcurrentModificationException异常。

无状态

大部分流的操作的参数都是函数式接口,可以使用Lambda表达式实现。它们用来描述用户的行为,称之为行为参数(behavioral parameters)。

如果这些行为参数有状态,则流的操作的结果可能是不确定的,比如下面的代码:

下面代码在并行执行时,由于执行顺序的不确定性,state.s的改变时机可能不同,导致多次执行时的结果不同

List<String> l = new ArrayList(Arrays.asList("one", "two", ……));
class State {
    boolean s;
}
final State state = new State();
Stream<String> sl = l.stream().map(e -> {
    if (state.s)
        return "OK";
    else {
        state.s = true;
        return e;
    } 
});
sl.forEach(System.out::println);

排序

某些流的返回元素是有确定顺序的。这取决于它的数据源和中间操作。

比如List和Array创建的流是有序的,而HashSet创建的流是无序的。

sort()方法可以将流变得有序,而unordered()可以将流无序(不会打散元素)

当流有序时,如果用户并不关心顺序,可以使用unordered显示的去掉流的有序约束,可以改善某些情况下的并行性能

三、创建Stream

常见创建流的方法:

  • 通过集合Collection的Stream()parallelStream():如list.parallelStream()、Arrays.asList(1,2,3).stream()。
  • 通过数组Arrays.stream(Object[])方法:如Arrays.stream(new int[]{1,2,3})。
  • 流的静态方法:如:Stream.of(Object[])IntStream.range(int, int)Stream.iterate(Object, UnaryOperator)。
  • 随机数流Random.ints()

四、中间操作

中间操作是延迟执行的,不会修改原始数据流,在终点操作开始时才会开始执行

1. distinct

根据流中元素的hashCode()equals()方法去除重复元素

List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 4, 5, 6);
list.stream().distinct().forEach(System.out::print); // 123456
System.out.println();
list.forEach(System.out::print); // 12344456

2. filter

接收predicate,过滤数据

// 筛选出偶数
List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 4, 5, 6);
list.stream().filter(e -> e % 2 == 0).forEach(System.out::print); // 24446

3. map

接收一个Function实例,重新印射数据

// 加1
List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 4, 5, 6);
list.stream().map(e -> e + 1).forEach(System.out::print); // 23455567

4. mapToDouble

类似map,不过结果一定要是Double类型。

类似的方法还有mapToInt(),mapToLong()

// 加1.1
List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 4, 5, 6);
list.stream().mapToDouble(e -> e + 1.1).forEach(System.out::println);
/*
2.1
3.1
4.1
5.1
5.1
5.1
6.1
7.1
*/
// 加1
List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 4, 5, 6);
list.stream().mapToDouble(e -> e + 1).forEach(System.out::println);
/*
2.0
3.0
4.0
5.0
5.0
5.0
6.0
7.0
*/

4. flatmap

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

将映射后的元素放在一个新的流中。

类似的方法还有flatMapToDoubleflatMapToIntflatMapToLong

List<String> list = Arrays.asList("1233", "2444", "562","78");
list.stream()
    .flatMap(e -> Arrays.stream(e.split("2")))
    .forEach(System.out::println);
/*
1
33

444
56
78
*/

5. limit

限制流中元素的数量。对于有序并行流,性能消耗更高

List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 4, 5, 6);
list.stream().limit(5).forEach(System.out::print); // 12344

6. skip

丢弃前n个元素,返回流。

System.out.println("-----------------------");
list.stream().skip(6).forEach(System.out::println);
System.out.println("-----------------------");
list.stream().skip(8).forEach(System.out::println);
/*
7
5
-----------------------
-----------------------
*/

7. peek

接收一个Consumer,对元素做某些操作,但是并不会修改数据(对象数据可能会被修改)。一般用于debug

List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);
list.stream().peek(System.out::print).count(); // 810975

8. sorted

将流中的元素按自然顺序排序。可以自定义比较器。

System.out.println("升序: ");
List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);
list.stream().sorted().forEach(System.out::print);
System.out.println();
System.out.println("降序: ");
list.stream().sorted((x, y) -> y - x).forEach(System.out::print);
/*
升序: 
015789
降序: 
987510
*/

五、终点操作

1. Match

allMatch(Predicate p)anyMatch(Predicate p)noneMatch(Predicate p)三种方法。

返回boolean

List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);
// allMatch 是否全部符合
System.out.println(list.stream().allMatch(x -> x > -1)); // true
System.out.println(list.stream().allMatch(x -> x > 1)); // false
// anyMatch 是否至少有一个符合
System.out.println(list.stream().anyMatch(x -> x == -1)); // false
System.out.println(list.stream().anyMatch(x -> x == 1)); // true
// noneMatch 是否全部不符合
System.out.println(list.stream().noneMatch(x -> x > 5)); // false
System.out.println(list.stream().noneMatch(x -> x > 9)); // true

2. count

返回流中元素的数量。返回long

List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);
System.out.println(list.stream().count()); // 6

3. collect

传入Collectors中的接口实现,将流转化为其他形式的数据。部分接口:

  • toListtoSet 转化为list或set
  • counting 计数
  • reducing 化简
  • maxByminBy 最大值最小值
  • summingInt 求和
  • joining 字符串连接
List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);

// 转化为list格式
List<Integer> res1 = list.stream().collect(Collectors.toList());

// 转化为set格式
Set<Integer> res2 = list.stream().collect(Collectors.toSet());

4. find

有findFirst()和findAny()两种。返回Optional对象

Optional<T> findFirst();
List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);
Optional<Integer> integer = list.stream().findFirst();
System.out.println(integer.get()); // 8

// findAny()对于串行流:一般返回第一个结果
integer = list.stream().findAny();
System.out.println(integer.get()); // 8

// findAny()对于并行流,多次执行会随机返回结果
integer = list.parallelStream().findAny();
System.out.println(integer.get()); // 随机打印

5. forEach

遍历流中元素,执行指定操作

List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);
List<Integer> res = new ArrayList<>();
list.stream().forEach(e -> res.add(e + 2));
System.out.println(list.toString());
// [10, 3, 2, 11, 9, 7]

6. max、min

最大值、最小值。返回Optional对象

将元素按比较器排序,然后min返回第一个数,max返回最后一个数

List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);

// 升序排序 015789
System.out.println(list.stream().min((x, y) -> x - y).get()); // 0
System.out.println(list.stream().max((x, y) -> x - y).get()); // 9

// 降序排序 987510
System.out.println(list.stream().min((x, y) -> y - x).get()); // 9
System.out.println(list.stream().max((x, y) -> y - x).get()); // 0

7. reduce

将元素数据结合后返回。

List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);

// 默认返回Optional类型数据
Optional<Integer> res1 = list.stream().reduce(Integer::sum);
System.out.println(res1.get());

// 指定返回数据类型为Integer
Integer res2 = list.stream().reduce(0, Integer::sum);
System.out.println(res2);

8. toArray()

将流中的元素保存到数组中

<A> A[] toArray(IntFunction<A[]> generator);
List<Integer> list = Arrays.asList(8, 1, 0, 9, 7, 5);
Integer[] ints = list.stream().map(e -> e + 1).toArray(Integer[]::new);
for (Integer integer : ints) {
    System.out.println(integer);
}
/*
9
2
1
10
8
6
*/

六、参考文档

  1. https://colobu.com/2016/03/02/Java-Stream/
  2. https://juejin.cn/post/6844903646648336397#heading-9

有关【java8】stream学习笔记的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  4. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  5. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  6. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  7. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  8. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  9. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  10. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

随机推荐