java8中stream流式编程的使用大全

news2024/11/8 16:35:07

一、概述

  • Stream流操作是Java 8提供一个重要新特性,它允许开发人员以声明性方式处理集合,其核心类库主要改进了对集合类的API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中,能 让代码更加简洁,极大地简化了集合的处理操作,提高了开发的效率和生产力。

  • 同时Stream不是一种数据结构,它只是某种数据源的一个视图,源可以是数组、文件、集合、函数,Java容器或I/O 通道(channel)等。在Stream中的操作每一次都会产生新的流,内部不会像普通集合操作一样立刻获取值,而是 惰性取值 ,只有等到用户真正需要结果的时候才会执行。

  • 并且对于现在调用的方法,本身都是一种高层次构件,与线程模型无关。因此在并行使用中,开发者们无需再去操 心线程和锁了。Stream内部都已经做好了 。

  • 对于Stream流的可以把它当成工厂中的流水线,每个Stream流的操作过程遵循着创建 -->操作 -->获取结果的过程,就像流水线上的节点一样组成一个个链条。除此之外你还可以把他理解成sql的视图,集合就相当于数据表中的数据,获取Stream流的过程就是确定数据表的属性和元数据的过程,元数据的每一个元素就是表中的数据,对Stream流进行操作的过程就是通过sql对这些数据进行查找、过滤、组合、计算、操作、分组等过程,获取结果就是sql执行完毕之后获取的结果视图一样。

二、Stream流的获取方法

1)单列集合

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();

2)双列集合

HashMap<String, Integer> map = new HashMap<>();

双列集合不能直接获取Stream流,一般有以下两种方式获取双列集合的Stream流:

  • 使用keyset

    先获取到所有的键,再把这个set集合中所有的键放到stream流中

    Stream<String> stream = map.keySet().stream();
    
    // 使用示例
    stream.forEach(key -> System.out.println(key + "=" + map.get(key)))
    
  • 使用entrySet

    先获取到所有的键值对对象,再把这个set集合中所有的键值对对象放到stream

    Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
        
    // 使用示例
    stream.forEach(item -> System.out.println(item.getKey() + '-' + item.getValue()));
    

3)数组

int[] arr = {1, 2, 3}
IntStream stream = Arrays.stream(arr)
    
// 使用示例
stream.forEach(System.out::println); 

4)同种数据类型的多个数据

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5)

// 使用示例
stream.forEach(System.out::println);

5)获取并行流

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> parallelStream = list.parallelStream();

streamparallelStream的简单区分:

  • stream是顺序流,由主线程按顺序对流执行操作

  • parallelStream是并行流,内部以多线程并行执行的方式操作流,前提是对流中的数据处理没有顺序要求。

例如筛选集合中的奇数,两者的处理有所不同:
在这里插入图片描述

6)文件

// lines方法需要抛出或者捕获异常,因为文件data.txt是有可能不存在的
Stream <String > lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());

7)通过函数生成

Stream提供了iterategenerate两个静态方法从函数中生成流。

  • iterate
// iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作
Stream<Integer> iterate = Stream.iterate(0, item -> item + 1);

// iterator生成的流为无限流,使用时需要进行截取
iterate.limit(10).forEach(item -> System.out.println(item));
  • generate
// generate方法接受一个参数,方法参数类型为Supplier,一个生产型的函数式接口
Stream<Double> generate = Stream.generate(Math::random);

// generate生成的流为无限流,使用时需要进行截取
generate.limit(10).forEach(item -> System.out.println(item));

三、Stream流的基本操作

3.1 中间操作

中间操作的相关方法执行完之后,会返回一个处理过后的Stream流,这样依然可以继续执行其它流操作。

形成一定的链式编程。

1)filter

对流中的数据进行过滤、筛选 。

List<Integer> list = Arrays.asList(6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream().filter(item -> item > 8)

2)distinct

// 普通数字集合去重
List<Integer> list = Arrays.asList(6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream().distinct();

// 对象集合根据某字段去重(根据图书名称去重)
Map<Object, Boolean> map = new HashMap<>();
List<Book> distinctNameBooks = books.stream()
				   .filter(item -> map.putIfAbsent(item.getName(), Boolean.TRUE) == null)
    			   .collect(Collectors.toList());

// 提取集合中的对象的某个字段属性并去重
List<String> nameList = pipeList.stream()
                                .map(Student::getName)
                                .distinct().collect(Collectors.toList());

putIfAbsent 方法:

如果传入key对应的value已经存在,就返回存在的value,不进行替换。

如果不存在,就添加key和value,返回null

3)limit

截取此流中的部分元素组成的流,截取前指定参数个数的。

List<Integer> list = Arrays.asList(6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream().limit(2);

4)skip

跳过指定参数个数的数据,返回由该流的剩余元素组成的流。

List<Integer> list = Arrays.asList(6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream().skip(3);

5)concat

合并两个流,返回合并后的新流。

List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> list2 = Arrays.asList(6, 7, 8, 9, 10);
Stream<Integer> concat = Stream.concat(list1.stream(), list2.stream());

6)map

流映射,可以将接受的元素映射成另外一个类型的元素。

List<String> stringList = Arrays.asList("lambda", "stream");
Stream<Integer> stream = stringList.stream().map(item -> item.length());

即使是复杂对象也是一样处理。

7)flatMap

流转换,可以将一个流中的每个值都转换为另一个流。

Stream<String[]> stream2 = stringList.stream().map(item -> item.split(""));
Stream<String> stringStream = stream2.flatMap(Arrays::stream);

8)allMatch

匹配所有,返回一个boolean类型的数据,意为流中元素是否都满足某个条件。

List<Integer> list = Arrays.asList(6, 7, 8, 9, 10);
if (list.stream().allMatch(item -> item > 5)) {
    System.out.println("集合中的所有元素都大于5");
}

9)anyMatch

匹配其中任意一个,返回一个boolean类型的数据,意为流中元素是否存在满足某个条件的元素。

List<Integer> list = Arrays.asList(6, 7, 8, 9, 10);
if (list.stream().anyMatch(item -> item > 5)) {
    System.out.println("集合中存在大于5的值");
}

10)noneMatch

全部不匹配,返回一个boolean类型的数据,意为流中元素是否都不满足某个条件。

List<Integer> list = Arrays.asList(6, 7, 8, 9, 10);
if (list.stream().noneMatch(item -> item > 5)) {
    System.out.println("集合中的所有元素小于或等于5");
}

3.2 终结操作

1)forEach

对此流的每个元素执行相同操作,和for循环效果类似。

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

2)count

统计出流中元素的个数。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
long count = list.stream().count();

3)counting

也是统计出流中元素个数,只不过这一般要与collect联合使用。

import static java.util.stream.Collectors.counting;

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Long count = list.stream().count();

4)findFirst

查找流中的第一个元素。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> first = list.stream().findFirst();

5)findAny

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> any = list.stream().findAny();

6)reduce

将流中的元素组合起来。

reduce方法的第一个参数是一个初始值,第二个参数是初始值和流中的各个元素挨个要进行的操作。

例如是求和,就是初始值依次按照求和规则加上前面的值。

第一个参数初始值是可选的,不一定要填。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = list.stream().reduce(0, (a, b) -> (a + b));

// 使用方法引用简写
Integer sum = list.stream().reduce(0, Integer::sum);
// 方法引用等同于如下写法
Integer sum2 = list.stream().reduce(0, (a, b) -> Integer.sum(a, b));

reduce更多应用可以看下面的各类求和、求最大最小值的部分,这里只介绍基本用法

3.3 收集操作

1)Collectors.toList()

在底层会创建一个List集合.并把所有的数据添加到List集合中.

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 2);
List<Integer> collect = list.stream().filter(number -> number % 2 == 0)
                					 .collect(Collectors.toList());

2)Collectors.toSet()

把流中的数据写使用set集合收集起来,使用set集合不会保留重复数据

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 2);
List<Integer> collect = list.stream().filter(number -> number % 2 == 0)
                					 .collect(Collectors.toSet());

3)Collectors.toMap()

List<Integer> list = Arrays.asList("zhangsan,23", "lisi,24");
// s 依次表示流中的每一个数据
// 第一个lambda表达式就是如何获取到Map中的键
// 第二个lambda表达式就是如何获取到Map中的值
Map<String, Integer> map = list.stream()
    						   .collect(Collectors.toMap(
                                   s -> s.split(",")[0],
                                   s -> Integer.parseInt(s.split(",")[1])
                                ));

四、其它常见操作

4.1 求和

1)给数值集合求和

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 方式一、使用lambda表达式
Integer sum1 = list.stream().reduce(0, (a, b) -> (a + b));

// 方式二、使用方法引用简写
Integer sum2 = list.stream().reduce(0, Integer::sum);
// 方法引用等同于如下写法
Integer sum2 = list.stream().reduce(0, (a, b) -> Integer.sum(a, b));

// 方式三、使用summingInt系列的方法(summingDouble、summingLong等)
Integer sum3 = list.stream().collect(summingInt(Integer::intValue))
    
// 方式四、推荐写法 (通过mapToInt将对象流转换为数值流,避免了装箱和拆箱操作,易读的同时保证了性能)
Integer sum4 = list.stream().mapToInt(item -> item).sum();

2)对集合中对象的某个字段求和

// 这里如果是整型或者是浮点数就用各自的方法即可,因为这样比较常规,所以这里以BigDecimal为例
List<Book> list = new ArrayList<>();
BigDecimal reduce = list.stream()
    						// 将Opportunity对象的金额属性取出
                           .map(Book::getCount)   
    						// 使用reduce()聚合函数,得到金额总和
                           .reduce(BigDecimal.ZERO, BigDecimal::add);   

4.2 求最大最小值

1)不使用reduce

// 普通数值集合
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> min1 = list.stream().min(Integer::compareTo);
Optional<Integer> max1 = list.stream().max(Integer::compareTo);
OptionalInt min2 = list.stream().mapToInt(Integer::valueOf).min();
OptionalInt max2 = list.stream().mapToInt(Integer::valueOf).max();

// 对象集合
List<Book> list = new ArrayList<>();
OptionalInt min3 = list.stream().mapToInt(Book::getCount).min();	// 推荐写法
OptionalInt max3 = list.stream().mapToInt(Book::getCount).max();	// 推荐写法
OptionalInt min4 = list.stream()
                       .mapToInt(Book::getCount)
                       .collect(minBy(Integer::compareTo));
OptionalInt max4 = list.stream()
                       .mapToInt(Book::getCount)
                       .collect(maxBy(Integer::compareTo));

推荐写法通过mapToInt将对象流转换为数值流,避免了装箱和拆箱操作,易读的同时保证了性能

2)使用reduce

List<Book> list = new ArrayList<>();
OptionalInt min = list.stream().map(Book::getCount).reduce(Integer::min);
OptionalInt max = list.stream().map(Book::getCount).reduce(Integer::max);

4.3 求平均值

List<Book> list = new ArrayList<>();
Double avg = list.stream().collect(averagingInt(Book::getCount));

4.4 同时求总和、平均值、最大值、最小值

List<Book> list = new ArrayList<>();
IntSummaryStatistics intSummaryStatistics = list.stream().collect(summarizingInt(Book::getCount));
int min = intSummaryStatistics.getMin();		// 获取最小值
int max = intSummaryStatistics.getMax();		// 获取最大值
long sum = intSummaryStatistics.getSum();		// 获取总和
Double avg = intSummaryStatistics.getAverage(); // 获取平均值

4.5 拼接流中的元素

// joining的方法参数为元素的分界符
List<String> stringList = Arrays.asList("lambda", "stream");
String result = stringList.stream().collect(Collectors.joining(","));
System.out.println(result);		//lambda,stream

4.6 分组

1)普通分组

List<Book> list = new ArrayList<>();
Map<String, List<Book>> list.stream().collect(groupingBy(Book::getName));

2)使用集合中的对象的某个字段分组并统计每组数量

Map<String, Long> collect = list.stream()
        			.collect(Collectors.groupingBy(ish::getName, Collectors.counting()));

4.7 分区

分区是特殊的分组,它分类依据是truefalse,所以返回的结果最多可以分为两组

List<Book> list = new ArrayList<>();
Map<Boolean, List<Book>> list.stream().collect(groupingBy(Book::isChina));

应用场景示例:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List<Integer>> collect2 = list.stream()
    									   .collect(partitioningBy(item -> item < 3));

输出结果是这样:{false=[3, 4, 5], true=[1, 2]}truefalse分为了两组;

4.8 排序

1)基本数据类型自然排序

List<Integer> list = Arrays.asList(2, 3, 1, 4, 5);
// 升序
List<Integer> collect = list.stream().sorted().collect(Collectors.toList());

// 降序
List<Integer> collect1 = list.stream().sorted(Comparator.reverseOrder())
    								  .collect(Collectors.toList());

2)按照对象属性进行排序

List<Student> list = new ArrayList<>();

// 按学生年龄倒序, 不加reversed()就是升序
List<Student> collect = list.stream()
                            .sorted(Comparator.comparing(Student::getAge).reversed())
                            .collect(Collectors.toList());


// 如果需要多条件排序,可以使用thenComparing()
// 例如先按照年龄排序,再按照姓名排序
List<Student> collect2 = list.stream()
                             .sorted(Comparator.comparing(Student::getAge)
                             .thenComparing(Comparator.comparing(Student::getName)))
                             .collect(Collectors.toList());

3) List<Map<String, Object>>集合按某个key进行排序

List<Map<String, Object>> dataList = new ArrayList<>();
// 准备两数据
dataList.add(new HashMap<String, Object>() {
    {
        put("id", "1");
        put("type", "0");
    }
});
dataList.add(new HashMap<String, Object>() {
    {
        put("id", "2");
        put("type", "2");
    }
});

// 升序
List<Map<String, Object>> newAscList = dataList.stream()
                        .sorted(Comparator.comparing((Map m) -> (new BigDecimal(m.get("question_type").toString()))))
                        .collect(Collectors.toList());

// 降序
List<Map<String, Object>> newDescList = dataList.stream()
                        .sorted(Comparator.comparing((Map m) -> (new BigDecimal(m.get("question_type").toString()))).reversed())
                        .collect(Collectors.toList());

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/185502.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Node.js教程笔记(四)数据库与身份认证

学习目标 1、能够知道如何配置Mysql数据库环境 2、能够认识并使用常见的SQL语句操作数据库 3、能够在Express中操作Mysql数据库 4、能够了解Session的实现原理 5、能够了解JWT的实现原理 目录 1、数据库的基本概念 2、安装并配置Mysql 3、Mysql的基本使用 4、在Expre…

全球数十万客户选择亚马逊云科技数据库服务构建自己的应用

日前&#xff0c;全球市场分析机构Gartner发布《2022云数据库管理系统魔力象限》报告&#xff08;2022 Gartner Magic Quadrant™ for Cloud Database Management Systems&#xff09;。Gartner数据显示&#xff0c;整个数据库管理系统市场在2021年实现强劲增长22.3%&#xff0…

【五六七人口普查】我国省市两级各行业门类人口及三次产业人口比重

人口数据是我们在各项研究中最常使用的数据&#xff01;之前我们分享过第七次人口普查&#xff08;简称七普&#xff09;的数据&#xff01;很多小伙伴拿到数据后都反馈数据非常好用&#xff0c;同时很多小伙伴咨询有没有前面几次人口普查的数据&#xff0c;这样方便做人口变化…

【Java】final关键字和final的四种用法

final定义 final翻译成中文的意思是 “最终” &#xff0c; 它是java当中的一个关键字&#xff0c;使用final修饰的对象不允许修改或替换其原始值或定义。 假如当final修饰一个类的时候&#xff0c;是不能被其他类继承的。 final的四种用法 修饰类修饰方法修饰变量修饰参数 1.…

会话保持技术:cookie、session

目录 1.概述 2.cookie 3.session 1.概述 会话保持技术的出现是因为HTTP 是一个无状态的协议&#xff0c;这一次请求和上一次请求是没有任何关系的&#xff0c;互相无法感知&#xff0c;上一次请求干了什么&#xff1f;这一次请求完全不知道&#xff0c;会话保持技术就是为了…

Python协程asyncio异步编程结合uvloop性能提升

asyncio介绍 和我们以前常用的gevent模块相似&#xff0c;asyncio模块也是在Python中实现协程的模块区别是gevent是第三方库&#xff0c;通过greenlet实现协程&#xff0c;遇到I/O自动切换&#xff08;自动挡&#xff09;asyncio是Python 3.4版本引入的标准库&#xff0c;asyc…

GitHub访问问题与FastGithub下载及使用(详细篇)

前言 &#x1f4dc; “ 作者 久绊A ” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴 目录 前言 FastGithub的介绍 FastGithub的下载 FastGithub的安装及…

23种设计模式之综合实战篇

23种设计模式之综合实战篇1. 设计模式概述1.1 什么是设计模式1.2 设计模式的好处2. 设计原则分类3. 详解3.1 单一职责原则3.2 开闭原则3.3 里氏代换原则3.4 依赖倒转原则3.5 接口隔离原则3.6 合成复用原则3.7 迪米特法则4. Awakening1. 设计模式概述 我们的软件开发技术也包括一…

微刊:做了很多的16S三代测序,文章可以这样写~

三代的黄金时代已经来临&#xff01;在扩增子方面与二代相比其优势明显&#xff1a;1&#xff1a;获得的序列更长&#xff0c;信息量更多更准确&#xff01;2;鉴定高精准&#xff0c;“种”水平物种鉴定中实现了大幅提升。3:PCR扩增无GC偏好性&#xff0c;数据更准确。4:V1-V9全…

MySQL多表操作的外键约束

目录 一.多表关系 一对一关系 一对多/多对一关系 多对多关系 二.外键约束 特点 创建外键约束 方式1-在创建表时设置外键约束 方式2-在创建表时设置外键约束 在外键约束下的操作 1.数据插入 2.删除数据 3.删除外键约束 外键约束-多对多关系 操作 一.多表关系 MySQL多表…

51单片机学习笔记-12LCD1602液晶屏

12 LCD1602液晶屏 [toc] 注&#xff1a;笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。 注&#xff1a;工程及代码文件放在了本人的Github仓库。 12.1 LCD1602介绍 LCD1602&#xff08;Liquid Crystal Display&#xff09;…

【Java 数据结构】实现一个二叉搜索树

目录 1、认识二叉搜索树 2、实现一个二叉搜索树 2.1 成员变量 2.2 insert 方法 2.3 search 方法 2.4 remove 方法(重点) 3、二叉搜索树总结 1、认识二叉搜索树 从字面上来看&#xff0c;它只比二叉树多了搜索两个字&#xff0c;我们回想一下&#xff0c;如果要是在二…

linux性能优化-CPU上下文切换

疑问&#xff1a;进程在竞争CPU时并没有真正运行&#xff0c;为什么还会导致系统的负载升高&#xff1f; 因为存在CPU上下文切换。 linux系统说明 Linux是一个多任务操作系统&#xff0c;它支持远大于CPU数量的任务同时运行。当然&#xff0c;这些任务实际上并不是真的在同时…

pytorch-lightning中使用wandb实现超参数搜索

由于最近涉及下游任务微调&#xff0c;预训练任务中的框架使用的是pytorch-lightning&#xff0c;使用了典型的VLP(vision-language modeling)的训练架构&#xff0c;如Vilt代码中&#xff1a;https://github.com/dandelin/ViLT&#xff0c;这类架构中只涉及到预训练&#xff0…

51单片机学习-5定时器与中断

5 定时器与中断 [toc] 注&#xff1a;笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。 注&#xff1a;工程及代码文件放在了本人的Github仓库。 5.1 定时器原理与中断系统 5.1.1 定时器原理 CPU的时序指标有&#xff1a; 振…

C语言预处理命令是什么?

C语言源文件要经过编译、链接才能生成可执行程序&#xff1a;1) 编译&#xff08;Compile&#xff09;会将源文件&#xff08;.c文件&#xff09;转换为目标文件。对于 VC/VS&#xff0c;目标文件后缀为.obj&#xff1b;对于GCC&#xff0c;目标文件后缀为.o。编译是针对单个源…

ESP32设备驱动-ADS1015(ADC)驱动

ADS1015(ADC)驱动 1、ADS1015介绍 ADS1015 是一款具有 12 位分辨率的精密模数转换器 (ADC),采用超小型无引线 QFN-10 封装或 MSOP-10 封装。 ADS1015 的设计考虑了精度、功率和易于实施。 ADS1015 具有板载基准和振荡器。 数据通过 I2C 兼容的串行接口传输; 可以选择四个 I…

Portapack应用开发教程(十八)NavTex接收 C

有段时间没研究NavTex了&#xff0c;这段时间打算捡起来继续搞。 上一篇文章中&#xff0c;我用frisnit生成了wav文件。然后再用gnuradio观察波形&#xff0c;发现波形确实能与frisnit中的描述以及python解码程序中的dictionary对应上。 接下来&#xff0c;我要重点想办法自己…

Rust机器学习之petgraph

Rust机器学习之petgraph 图作为一种重要的数据结构和表示工具在科学和技术中无处不在。因此&#xff0c;许多软件项目会以各种形式用到图。尤其在知识图谱和因果AI领域&#xff0c;图是最基础的表达和研究工具。Python有著名的NetworksX库&#xff0c;便于用户对复杂网络进行创…

apt命令详解

apt&#xff08;Advanced Packaging Tool&#xff09;是一个在 Debian 和 Ubuntu 中的 Shell 前端软件包管理器。 apt 命令提供了查找、安装、升级、删除某一个、一组甚至全部软件包的命令&#xff0c;而且命令简洁而又好记。 apt 命令执行需要超级管理员权限(root)。前些日子…