news 2026/6/19 18:36:07

Stream流全面讲解(流的思想,获取流,中间方法,终结方法等等,包含综合对应练习题)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Stream流全面讲解(流的思想,获取流,中间方法,终结方法等等,包含综合对应练习题)

(1)Stream流的思想和获取Stream流

Stream流的思想

流水线思想,java.util.stream.Stream<T>是函数式接口,Stream本身是接口。

Stream流的作用

结合了Lambda表达式,简化集合、数组的操作

Stream流的使用步骤:
  1. 先得到一条Stream流(流水线),并把数据放上去

  2. 利用Stream流中的API进行各种操作(

过滤,转换 ->中间方法方法调用完毕之后,还可以调用其他方法

统计,打印 ->终结方法最后一步,调用完毕之后,不能调用其他方法

所以:

  1. 先得到一条Stream流(流水线),并把数据放上去

  2. 使用中间方法对流水线上的数据进行操作

  3. 使用终结方法对流水线上的数据进行操作

获取Stream流的方式

单列集合 default Stream<E> stream() Collection中的默认方法

双列集合 无 无法直接使用stream流

数组 public static<T> Stream<T> stream(T[] array) Arrays工具类中的静态方法

一堆零散数据 public static<T> Stream<T> of(T...values) Stream接口中的静态方法

细节:
  • 单列集合通过Collection中的默认方法stream()方法获取Stream流

  • 双列集合无法直接获取,需通过keySet()方法或者entrySet()方法获取对应的单列Set集合,然后调用单列集合中的stream方法

  • 数组可以通过Arrays工具类中的静态方法stream(T[] array)获取stream流

  • 一堆零散的数据,注意这些数据的数据类型一致,可以通过Stream接口中的of方法获取stream流

这里Stream接口中静态方法of(T...values)方法的细节

//方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组

//但是数组必须是引用数据类型的,如果传递基本数据类型,则会把整个数组当做一个元素,放到Stream当中,打印时会打印该数组的地址。

import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Stream; public class StreamDemo1 { public static void main(String[] args) { //单列集合通过stream()方法获取Stream流 ArrayList<String> list = new ArrayList<>(); Collections.addAll(list,"a","b","c","d","e"); Stream<String> stream1 = list.stream(); stream1.forEach(s->System.out.println(s)); //双列集合无法直接获取 HashMap<String, Integer> hm = new HashMap<>(); hm.put("aaa",111); hm.put("bbb",222); hm.put("ccc",333); hm.put("ddd",444); //第一种获取stream流的方式Set<String>存储键的Set集合 hm.keySet().stream().forEach(key->System.out.println(key)); //第二种获取stream流的方式 hm.entrySet().stream().forEach(entry->System.out.println(entry.getKey()+":"+entry.getValue())); // hm.entrySet().stream().forEach(new Consumer<Map.Entry<String, Integer>>() { // @Override // public void accept(Map.Entry<String, Integer> entry) { // System.out.println(entry.getKey()+":"+entry.getValue()); // } // }); //数组获取stream流的方式通过Arrays工具类中的静态方法stream(T[] array)方法获取 int[] arr1 = {1,2,3,4,5}; Arrays.stream(arr1).forEach(i->System.out.println(i)); String[] arr2 = {"a","b","c","d","e"}; Arrays.stream(arr2).forEach(s->System.out.println(s)); //一堆零散的数据获取stream流,只不过这些零散的数据类型相同,通过Stream接口中的静态方法of(T...values);获取 Stream.of(1,2,3,4).forEach(i->System.out.println(i)); Stream.of("a","b","c","d","e").forEach(s->System.out.println(s)); } }

(2)Stream流的中间方法

中间方法

Stream<T>filter(Predicate<? super T> predicate) 过滤

Stream<T>limit(long maxSize) 获取前几个元素

Stream<T>skip(long n) 跳过前几个元素

Stream<T>distinct() 元素去重,依赖(hashCode和equals方法)

static <T>Stream<T>concat(Stream a,Stream b) 合并a和b两个流为一个流

Stream<R>map(Function<T ,R> mapper) 转换流中的数据类型

注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次(再次使用会出现IllegalStateException异常),建议使用链式编程

注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据

1、过滤

filter里面的参数 Predicate是一个函数式接口,所以要实现里面的public boolean test(T a) {}方法

例 public boolean test(String s){return s.startsWith("张");}

//如果返回值为true,表示当前数据留下

//如果返回值为false,表示当前数据舍弃不要

2、元素去重

distinct()底层是依赖HashSet,所以依赖hashCode和equals方法,如果泛型类型是自定义对象,那么需要在自定义类中进行重写方法。

3、转换流中的数据类型

map参数类型为函数式接口类型(Function),需要实现类实现该接口,然后通过new 这个实现类传递参数,但是JDK8以后的匿名内部类实现了这一复杂过程的简化,之后的Lambda表达式再次简化匿名内部类的书写

例://Function函数式接口,泛型约束的类型,第一个类型:流中原本的数据类型,第二个类型:要转成之后的类型 //apply返回值:表示转换之后的数据 //当map方法执行完毕之后,流上的数据就变成了整数

list4.stream().map(new Function<String, Integer>() { @Override public Integer apply(String s) { String[] arr = s.split("-"); String ageString = arr[1]; int age = Integer.parseInt(ageString); return age; } }).forEach(age-> System.out.println(age));
import java.util.ArrayList; import java.util.Collections; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; public class StreamDemo2 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); Collections.addAll(list,"刘备","孙尚香","孙权","孙策","孙仲谋","孙膑","马超"); //filter过滤,参数为函数式接口对象,返回值为true保留,返回值为false不保留 list.stream().filter(new Predicate<String>() { @Override public boolean test(String s) { return s.startsWith("孙"); } }).forEach(s->System.out.println(s)); Stream<String> stream1=list.stream().filter(s->s.startsWith("孙")); Stream<String> stream2=stream1.filter(s->s.length() == 3); stream2.forEach(s->System.out.println(s)); System.out.println("++++++++++++++++++++++++++++++++++++++++++++"); //Stream<String> stream3 = stream1.filter(s->s.length()==3); list.stream().filter(s->s.startsWith("孙")).filter(s->s.length() == 3).forEach(s->System.out.println(s)); //limit(int n)获取流中前n个元素 list.stream().limit(3).forEach(s->System.out.println(s)); System.out.println("+++++++++++++++++++++++++++++++++"); //skip(int n)跳过lq中前n个元素 list.stream().skip(4).forEach(s->System.out.println(s)); System.out.println("+++++++++++++++++++++++++++++++++++"); list.stream().limit(5).skip(2).forEach(s->System.out.println(s)); ArrayList<String> list1 = new ArrayList<>(); Collections.addAll(list1,"孙尚香","孙权","孙策","孙仲谋","孙膑","孙权","孙尚香"); ArrayList<String> list2 = new ArrayList<>(); Collections.addAll(list2,"刘备","张飞","关羽"); //distinct 元素去重,依赖(hashCode和equals方法); list1.stream().distinct().forEach(s->System.out.println(s)); System.out.println("+++++++++++++++++++++++++++++"); //Stream接口中的静态方法concat,合并两个stream流 Stream.concat(list1.stream(),list2.stream()).forEach(s->System.out.println(s)); ArrayList<String> list4 = new ArrayList<>(); Collections.addAll(list4,"刘备-32","孙尚香-18","孙权-19","孙策-28","孙仲谋-47","孙膑-81","马超-24"); //Function函数式接口,泛型约束的类型,第一个类型:流中原本的数据类型, 第二个类型:要转成之后的类型 //apply返回值:表示转换之后的数据 //当map方法执行完毕之后,流上的数据就变成了整数 list4.stream().map(new Function<String, Integer>() { @Override public Integer apply(String s) { String[] arr = s.split("-"); String ageString = arr[1]; int age = Integer.parseInt(ageString); return age; } }).forEach(age-> System.out.println(age)); } }

(3)Stream流的终结方法

终结方法

voidforEach(Consumer action) 遍历

Longcount() 统计

toArray() 收集流中的数据,放到数组中

collect(Collector collector) 收集流中的数据,放到集合中

——1、收集流中的数据,放到数组中

String[] arr = list.stream().toArray(new IntFunction<String[]>() {@Overridepublic String[] apply(int value) {return new String[value];}});System.out.println(Arrays.toString(arr));

//有参//IntFunction的泛型:具体类型的数组//apply的形参:流中数据的个数,要跟数组的长度保持一致//apply的返回值:具体类型的数组//方法体:就是创建数组//toArray方法的参数的作用:负责创建一个指定类型的数组//toArray方法的底层,会依次得到流里面的每个数据,并把数据放到数组当中//toArray方法的返回值:是一个装着流里面所有数据的数组

toArray()方法引用:toArray(Integer[] :: new);

方法引用:引用数组的构造方法

格式数据类型[]::new

范例int[]::new

引用数组的构造方法就是为了创建数组。

import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.function.Consumer; import java.util.function.IntFunction; public class StreamDemo3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); Collections.addAll(list,"刘备","孙尚香","孙权","孙策","孙仲谋","孙膑","马超"); list.stream().forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }); //统计 long count() long count=list.stream().count(); System.out.println(count); //toArray() 收集流里面的数据,把收集到的数据放到数组中 //无参 Object[] arr1 = list.stream().toArray(); System.out.println(Arrays.toString(arr1)); //有参 //IntFunction的泛型:具体类型的数组 //apply的形参:流中数据的个数,要跟数组的长度保持一致 //apply的返回值:具体类型的数组 //方法体:就是创建数组 //toArray方法的参数的作用:负责创建一个指定类型的数组 //toArray方法的底层,会依次得到流里面的每个数据,并把数据放到数组当中 //toArray方法的返回值:是一个装着流里面所有数据的数组 String[] arr = list.stream().toArray(new IntFunction<String[]>() { @Override public String[] apply(int value) { return new String[value]; } }); System.out.println(Arrays.toString(arr)); String[] arr2 = list.stream().toArray( value-> new String[value]); System.out.println(Arrays.toString(arr2)); } }

——2、收集流中的数据,放到集合中

collect(Collector collector) 收集流中的数据,放到集合中(List Set Map)

Collectorsjava.util.stream包下的一个工具类,全称java.util.stream.Collectors

它是一个工具类,里面全是静态方法,专门用来配合Stream.collect()做收集操作。

常用的静态方法有:

  • Collectors.toList():收集为List

  • Collectors.toSet():收集为Set

  • Collectors.toMap():收集为Map

toMap的两个参数为Function函数式接口对象参数一表示键的生成规则,参数二表示值的生成规则,具体看下面的代码及详解

import java.util.*; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; public class StreamDemo4 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20", "张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41" ); //收集List集合道中,把所有的男性收集起来。 List<String> newList1 = list.stream().filter(new Predicate<String>(){ @Override public boolean test(String s) { return "男".equals(s.split("-")[1]); } }).collect(Collectors.toList()); System.out.println(newList1); List<String> newList11 = list.stream() .filter( s-> "男".equals(s.split("-")[1])) .collect(Collectors.toList()); //收集到Set集合当中,把所有男生 Set<String> newList2 = list.stream().filter(new Predicate<String>() { @Override public boolean test(String s) { return "男".equals(s.split("-")[1]); } }).collect(Collectors.toSet()); System.out.println(newList2); Set<String> newList22 = list.stream() .filter( s-> "男".equals(s.split("-")[1])) .collect(Collectors.toSet()); //收集到Map集合中,把所有的男生收集起来,键:姓名,值:年龄 /*toMap : 参数一表示键的生成规则 参数二表示值的生成规则 参数一: Function泛型一:表示流中每一个数据的类型 泛型二:表示Map集合中键的数据类型 方法apply形参:依次表示流里面的每一个数据 方法体:生成键的代码 返回值:已经生成的键 参数二: Function泛型一:表示流中的每一个数据的类型 泛型二:表示Map集合中值的数据类型 方法apply形参:依次表示流里面的每一个数据 方法体:生成值的代码 返回值:已经生成的值 */ Map<String,Integer> m = list.stream().filter(s->"男".equals(s.split("-")[1])) .collect(Collectors.toMap(new Function<String,String>() { @Override public String apply(String s) { return s.split("-")[0]; } }, new Function<String, Integer>() { @Override public Integer apply(String s) { return Integer.parseInt(s.split("-")[2]); } } )); System.out.println(m); Map<String,Integer> m1 = list.stream() .filter(s->"男".equals(s.split("-")[1])) .collect(Collectors.toMap( s-> s.split("-")[0], s-> Integer.parseInt(s.split("-")[2]))); } }

(4)Stream流总结

(5)Stream流练习

——1、数字的过滤

import java.util.ArrayList; import java.util.Collections; import java.util.function.Predicate; public class StreamDemo5{ public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); Collections.addAll(list,1,2,3,4,5,6,7,8,9,10); list.stream().filter(new Predicate<Integer>() { @Override public boolean test(Integer n) { return n % 2 == 0 ? false : true; } }).forEach(i -> System.out.println(i)); list.stream() .filter(n-> n % 2 == 0 ? false : true) .forEach(i -> System.out.println(i)); } }

——2、字符串过滤并收集

import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; public class StreamDemo6 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); Collections.addAll(list,"孙权,23","孙尚香,19","孙膑,81","孙策,35","孙仲谋,51","马超,23","周瑜,32","瑶,18"); Map<String, Integer> map = list.stream().filter(new Predicate<String>() { @Override public boolean test(String s) { String ageStr = s.split(",")[1]; Integer age = Integer.parseInt(ageStr); return age >=24; } }).collect(Collectors.toMap(new Function<String, String>() { @Override public String apply(String s) { return s.split(",")[0]; } },new Function<String, Integer>() { @Override public Integer apply(String s) { return Integer.parseInt(s.split(",")[1]); } })); System.out.println(map); Map<String, Integer> m = list.stream() .filter( s-> {return Integer.parseInt(s.split(",")[1]) >=24; }) .collect(Collectors.toMap(s-> s.split(",")[0], s-> Integer.parseInt(s.split(",")[1]))); } }

——3、自定义对象的过滤收集

import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; public class StreamDemo7 { public static void main(String[] args) { ArrayList<String> list1 = new ArrayList<>(); Collections.addAll(list1,"马超-23","赵子龙-29","关羽-40","张飞-38","曹操-43","牢大-35"); ArrayList<String> list2 = new ArrayList<>(); Collections.addAll(list2,"孙尚香-19","瑶-16","孙美姬-23","王昭君-25","杨玉环-31","孙樱-20"); Stream<String> stream1 = list1.stream().filter(new Predicate<String>() { @Override public boolean test(String s) { return s.split("-")[0].length()==2; } }).limit(2); Stream<String> stream2 = list2.stream().filter(new Predicate<String>() { @Override public boolean test(String s) { return s.split("-")[0].startsWith("孙"); } }).skip(1); List<Student> list = Stream.concat(stream1,stream2).map(new Function<String, Student>() { @Override public Student apply(String s) { Student stu = new Student(); stu.setName(s.split("-")[0]); stu.setAge(Integer.parseInt(s.split("-")[1])); return stu; } }).collect(Collectors.toList()); System.out.println(list); } } class Student { String name; int age; public Student() {} public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/19 18:30:21

Windows防撤回神器:3分钟解锁微信/QQ消息完整查看权限

Windows防撤回神器&#xff1a;3分钟解锁微信/QQ消息完整查看权限 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁&#xff08;我已经看到了&#xff0c;撤回也没用了&#xff09; 项目地址: https://gitcode.co…

作者头像 李华
网站建设 2026/6/19 18:25:28

死锁分析进阶:从日志到根因,一次搞定死锁排查

​关键词​&#xff1a;死锁&#xff1b;InnoDB&#xff1b;锁等待&#xff1b;间隙锁&#xff1b;死锁日志&#xff1b;死锁预防 大家好&#xff0c;我是小耶&#xff0c;写功课只是为了我踩过的坑&#xff0c;你们别再踩了&#xff01; 半夜两点&#xff0c;手机响了。钉钉群…

作者头像 李华
网站建设 2026/6/19 18:20:58

MC68HC908JG16系统模块深度解析:SIM、MON与TIM实战指南

1. 项目概述&#xff1a;深入MCU的“神经中枢”与“后门”在嵌入式开发的世界里&#xff0c;尤其是面对像MC68HC908JG16这类经典的8位微控制器&#xff0c;很多开发者往往把精力集中在应用层的逻辑实现上&#xff0c;比如驱动外设、处理数据流。然而&#xff0c;真正决定一个系…

作者头像 李华
网站建设 2026/6/19 18:19:23

3D拓扑优化技术在宽带闪耀超表面设计中的应用

1. 宽带闪耀超表面设计中的3D拓扑优化技术解析在光学器件设计领域&#xff0c;传统闪耀光栅长期受限于锯齿状轮廓的加工难度和带宽性能瓶颈。我们团队最近成功将3D拓扑优化技术应用于宽带闪耀超表面设计&#xff0c;通过有限元建模与伴随灵敏度分析的创新组合&#xff0c;实现了…

作者头像 李华
网站建设 2026/6/19 18:16:59

ZYXWZ 远程连接工具实现白名单安全访问连接MySQL

在远程开发与运维场景中&#xff0c;数据库的安全访问始终是核心问题之一。传统的公网直连 MySQL 往往面临端口暴露、暴力破解、IP 不固定以及权限难以精细控制等风险&#xff0c;给系统安全带来较大隐患。 本文将介绍如何基于 ZYXWZ 远程连接工具 构建一套“白名单安全访问”方…

作者头像 李华
网站建设 2026/6/19 18:16:11

如何免费实现专业级直播抠像:obs-backgroundremoval插件完全指南

如何免费实现专业级直播抠像&#xff1a;obs-backgroundremoval插件完全指南 【免费下载链接】obs-backgroundremoval An OBS plugin for removing background in portrait images (video), making it easy to replace the background when recording or streaming. 项目地址…

作者头像 李华