Stream流
1. 概述
Stream流是Java 8引入的一种新的数据处理方式,它可以用来处理集合(包括单列集合和双列集合)和数组中的数据。它提供了一种优雅、声明式的方式来对数据进行操作和处理,简化了对集合和数组的遍历、过滤、映射、排序等操作。
Stream流可以被认为是一种高级版本的迭代器。
- 与传统的集合迭代器相比,Stream流具有以下特点:
-
Stream流不会对原有的数据集合进行修改,而是通过生成一个新的Stream流来进行操作。这种特性称为“惰性求值”,也就是说在对Stream流的操作时,并不会立即执行,只有在终止操作(如
forEach
、collect
等)被调用时才会真正执行计算过程。 -
Stream流支持并发操作,可以使用并行流来进行高效的多线程处理。通过并行化操作,可以极大地提高处理大数据集的效率。
-
Stream流的使用步骤:
- 创建Stream流
可以通过集合类的
stream()
方法、数组的stream()
方法,或者使用Stream.of()
方法来创建。- 进行一系列中间操作
通过调用Stream流的方法来对数据进行过滤、映射、排序等操作,这些操作会返回一个新的Stream流。
- 执行终止操作
使用终止操作来触发对Stream流的计算。常见的终止操作包括
forEach
(遍历每个元素执行特定的操作)、collect
(将元素收集到一个结果容器中)、reduce
(归约操作,将多个元素合并为一个结果)等等。 -
代码示例
按照下面的要求完成集合的创建和遍历“张三丰”,“张无忌”,“张翠山”,“王二麻子”,“张良”,“谢广坤”
-
创建一个集合,存储多个字符串元素
-
把集合中所有以"张"开头的元素存储到一个新的集合
-
把"张"开头的集合中的长度为3的元素存储到一个新的集合
-
遍历上一步得到的集合
package text.text02; import java.util.ArrayList; import java.util.Collections; import java.util.function.Consumer; /*初爽Stream流 按照下面的要求完成集合的创建和遍历 "张三丰","张无忌","张翠山","王二麻子","张良","谢广坤" - 创建一个集合,存储多个字符串元素 - 把集合中所有以"张"开头的元素存储到一个新的集合 - 把"张"开头的集合中的长度为3的元素存储到一个新的集合 - 遍历上一步得到的集合 */ public class text62 { public static void main(String[] args) { //未使用Stream流 System.out.println("未使用Stream流:"); method1(); //使用了Stream流 System.out.println("使用了Stream流:"); method2(); } //使用了Stream流 private static void method2() { //创建集合并添加字符串元素 ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤"); list.stream() .filter(name -> name.startsWith("张")) .filter(name -> name.length() == 3) .forEach(new Consumer<String>() { @Override public void accept(String name) { System.out.print(name + " "); } }); } //未使用Stream流 public static void method1() { //创建集合并添加字符串元素 ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤"); //遍历集合获取以“张”开头的元素,并存在新的集合中 ArrayList<String> list1 = new ArrayList<>(); for (String name : list) { if (name.startsWith("张")) { list1.add(name); } } System.out.println("所有以\"张\"开头的元素存储到一个新的集合:" + list1); //[张三丰, 张无忌, 张翠山, 张良] //把"张"开头的集合中的长度为3的元素存储到一个新的集合 ArrayList<String> list2 = new ArrayList<>(); for (String name : list1) { if (name.length() == 3) { list2.add(name); } } System.out.println("把\"张\"开头的集合中的长度为3的元素存储到一个新的集合:" + list2); //[张三丰, 张无忌, 张翠山] } }
-
-
输出结果
-
未使用Stream流
-
使用了Stream流
-
2. 方法
2.1 开始生成方法
2.1.1 概述
Stream流开始生成方法是指用于创建和获取Stream流的一些方法。在Java中,Stream是一种处理数据集合的抽象概念,它提供了丰富的数据操作和处理功能。
为了使用Stream,我们需要首先生成或获取到一个Stream流对象。
-
生成Stream流的方式
-
Collection体系集合
使用默认方法stream()生成流, default Stream stream()
-
Map体系集合
把Map转成Set集合,间接的生成流
-
数组
通过Arrays中的静态方法stream生成流
-
同种数据类型的多个数据
通过Stream接口的静态方法of(T… values)生成流
-
2.1.2 方法
获取方式 | 方法名 | 说明 |
---|---|---|
单列集合 | default Streamstream() | Collection中的默认方法 |
双列集合 | 无 | 无法直接使用stream流 |
数组 | public static Stream stream(T[] array) | Arrays工具类中的静态方法 |
-堆零散数据 | public staticStream of(T… values) | Stream接口中的静态方法 |
细节:
-
单列集合:
default Stream<E> stream()
,此方法是Collection
接口中的默认方法,用于将单列集合(如List、Set)转换为Stream流。例如:
List<String> list = Arrays.asList("A", "B", "C"); Stream<String> stream = list.stream();
-
双列集合:目前没有直接使用
stream()
方法将双列集合(如Map)转换为Stream流的方式。但是可以通过entrySet().stream()
或keySet().stream()
等方式间接获得Stream流,以对双列集合进行操作。 -
数组:
public static <T> Stream<T> stream(T[] array)
,此方法是Arrays
工具类中的静态方法,用于将数组转换为Stream流。例如:
String[] array = {"A", "B", "C"}; Stream<String> stream = Arrays.stream(array);
-
散零数据:
public static <T> Stream<T> of(T... values)
,此方法是Stream
接口中的静态方法,用于将一系列散零的数据转换为Stream流。例如:
Stream<String> stream = Stream.of("A", "B", "C");
2.1.3 代码示例
-
代码示例1:单列集合
package text.text02; import java.util.ArrayList; import java.util.Collections; import java.util.function.Consumer; import java.util.stream.Stream; /*生成方法: 单列集合 | default Stream<E>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接口中的静态方法 | */ public class text63 { public static void main(String[] args) { //完整模式 System.out.println("完整模式:"); method1(); //aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk System.out.println(); //简写模式 System.out.println("简写模式:"); method2(); //aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk } //简写模式 private static void method2() { //创建单列集合 ArrayList<String> list = new ArrayList<>(); //添加数据 Collections.addAll(list, "aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj", "kkk"); //创建Stream流并输出 list.stream() .forEach(s -> System.out.print(s + " ")); } public static void method1() { //创建单列集合 ArrayList<String> list = new ArrayList<>(); //添加数据 Collections.addAll(list, "aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj", "kkk"); //创建Stream流 Stream<String> stream = list.stream(); //输出数据 stream.forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.print(s + " "); } }); } }
-
输出结果:单列集合
-
代码示例2:双列集合
package text.text02; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.Consumer; /*生成方法: 双列集合 | 无 双列集合无stream流,必须先利用keySet()或者entrySet()方法转化成单列集合才可以使用 | 获取方式 | 方法名 | 说明 | | ----------- | --------------------------------------------- | ------------------------ | | 单列集合 | default Stream<E>stream() | Collection中的默认方法 | | 双列集合 | 无 | 无法直接使用stream流 | | 数组 | public static <T> Stream<T> stream(T[] array) | Arrays工具类中的静态方法 | | -堆零散数据 | public static<T>Stream<T> of(T... values) | Stream接口中的静态方法 | */ public class text64 { public static void main(String[] args) { //创建双列集合 HashMap<String, Integer> hm = new HashMap<>(); //添加数据 hm.put("手机", 1003); hm.put("电脑", 1005); hm.put("平板", 1001); hm.put("汽车", 1002); hm.put("飞机", 1004); //利用keySet()转化成单列集合 System.out.println("===============利用keySet()转化成单列集合==============="); method1(hm); //利用entrySet()方法转化成单列集合 System.out.println("===============利用entrySet()方法转化成单列集合==============="); method2(hm); } //利用entrySet()方法转化成单列集合 private static void method2(HashMap<String, Integer> hm) { //将双列集合转成单列集合(entrySet()方法) Set<Map.Entry<String, Integer>> entries = hm.entrySet(); entries.stream() .forEach(new Consumer<Map.Entry<String, Integer>>() { @Override public void accept(Map.Entry<String, Integer> stringIntegerEntry) { System.out.println(stringIntegerEntry); } }); } //利用keySet()转化成单列集合 private static void method1(HashMap<String, Integer> hm) { //将双列集合转成单列集合(keySet()方法) Set<String> set = hm.keySet(); set.stream() .forEach(new Consumer<String>() { @Override public void accept(String key) { Integer value = hm.get(key); System.out.println(key + " = " + value); } }); } }
-
输出结果:双列集合
-
利用keySet()转化成单列集合
-
利用entrySet()方法转化成单列集合
-
-
代码示例3:数组
package text.text02; import java.util.Arrays; import java.util.stream.Stream; /*生成方法: 数组 | public static <T> Stream<T> stream(T[] array) 注意:Stream接口中的静态方法of的细节: 方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组 但是数组只能是引用数据类型的,如果传递基本数据类型的,是会把整个数组当成一个元素,放到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接口中的静态方法 | */ public class text65 { public static void main(String[] args) { //创建数组 int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; //使用Arrays里的stream方法 Arrays.stream(arr) .forEach(s -> System.out.println(s)); //直接使用Stream里的of方法 //注意:Stream接口中的静态方法of的细节 //方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组 //但是数组只能是引用数据类型的,如果传递基本数据类型的,是会把整个数组当成一个元素,放到Stream流中 Stream.of(arr) .forEach(s -> System.out.println(s)); //[I@3d075dc0 } }
-
输出结果:数组
-
使用静态Stream方法
-
直接使用Stream里的of方法
-
-
代码示例4:一堆零散数据
package text.text02; import java.util.function.Consumer; import java.util.stream.Stream; /*生成方法: 一堆零散数据 | public static<T>Stream<T> of(T... values) 必须是同一种数据类型 | 获取方式 | 方法名 | 说明 | | ----------- | --------------------------------------------- | ------------------------ | | 单列集合 | default Stream<E>stream() | Collection中的默认方法 | | 双列集合 | 无 | 无法直接使用stream流 | | 数组 | public static <T> Stream<T> stream(T[] array) | Arrays工具类中的静态方法 | | -堆零散数据 | public static<T>Stream<T> of(T... values) | Stream接口中的静态方法 | */ public class text66 { public static void main(String[] args) { //创建stream流并输出 Stream.of("aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj", "kkk") .forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }); } }
-
输出结果:一堆零散数据
2.2 中间操作方法
2.2.1 概述
当处理 Java 8+ 中的 Stream 流时,中间操作方法用于对流中的元素进行处理和转换。
中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作
2.2.2 方法
方法名 | 说明 |
---|---|
Stream filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤。只保留满足给定条件的元素。 |
Stream limit(long maxSize) | 获取流中的前N个元素,如果流中的元素数量小于N,则返回流中的所有元素。 |
Stream skip(long n) | 跳过流中的前N个元素,返回剩余的元素。如果流中的元素数量小于N,则返回空流。 |
Stream distinct() | 对流中的元素进行去重操作,去重依赖元素的hashCode和equals方法。如果是自定义对象,需要重写hashCode和equals方法。 |
static Stream concat(Stream a, Stream b) | 将两个流a和b合并为一个流,按照a流的顺序先输出,然后按照b流的顺序输出。 |
Stream map(Function<T,R> mapper) | 对流中的每个元素进行映射操作,将其转换为另一种类型的元素,通过给定的函数进行转换。 |
- 注意1: 中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
- 注意2: 修改Stream流中的数据,不会影响原来集合或者数组中的数据
细节:
- filter方法获取流中的每一个数据.而test方法中的s就依次表示流中的每一个数据.我们只要在test方法中对s进行判断就可以了.
-
如果判断的结果为true,则当前的数据留下
-
如果判断的结果为false,则当前数据就不要.
-
2.2.3 代码示例
- 代码示例
package text.text02; import java.util.ArrayList; import java.util.Collections; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; /*操作方法: | 方法名 | 说明 | ----------------------------------------------- | ----------------------------------------------------- | Stream<T> filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤 | Stream<T> limit(long maxSize) | 获取前几个元素 | Stream<T> skip(long n) | 跳过前几个元素 | Stream<T> distinct() | 元素去重,依赖(hashcode和equals方法)(如果是自定义对象,必须重写hashcode和equals方法) | static<T> Stream<T>concat(Stream a, Stream b) | 合并a和b两个流为一个流 | Stream<R> map(Function<T,R> mapper) | 转换流中的数据类型 - 注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程 - 注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据 */ public class text67 { public static void main(String[] args) { //创建集合并添加数据 ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "吕布", "吕布", "吕布", "吕布", "吕布", "貂蝉", "小乔", "孙策", "大乔", "露娜", "刘备", "孙悟空", "凯", "亚瑟", "刘邦", "项羽", "张飞", "武则天", "钟无艳", "孙膑", "赵云", "妲己", "公孙离"); //1.Stream<T> filter(Predicate<? super T> predicate): 用于对流中的数据进行过滤 System.out.println("==========1.Stream<T> filter(Predicate<? super T> predicate): 用于对流中的数据进行过滤=========="); System.out.println("以“张”开头的姓名有:"); //获取Stream流对象 list.stream() //调用Stream流中的filter()过滤方法 .filter(new Predicate<String>() { @Override //filter方法获取流中的 每一个数据. //而test方法中的s就依次表示流中的每一个数据. //我们只要在test方法中对s进行判断就可以了. //如果判断的结果为true,则当前的数据留下 //如果判断的结果为false,则当前数据就不要. public boolean test(String s) { return s.startsWith("孙"); } }) //遍历输出Stream流 .forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.print(s + " "); } }); System.out.println(); //2.Stream<T> limit(long maxSize) : 获取前几个元素 System.out.println("==========2.Stream<T> limit(long maxSize) : 获取前几个元素=========="); System.out.println("获取前5个元素是:"); //获取Stream流对象 list.stream() //调用Stream流中的limit()方法 .limit(5) //遍历Stream流 .forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.print(s + " "); } }); System.out.println(); //3.Stream<T> skip(long n):跳过前几个元素 System.out.println("==========3.Stream<T> skip(long n):跳过前几个元素=========="); System.out.println("跳过前5个元素,剩下的元素是:"); //获取Stream流对象 list.stream() //调用Stream中的skip()方法 .skip(5) //遍历Stream流 .forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.print(s + " "); } }); System.out.println(); //4.Stream<T> distinct() :元素去重,依赖(hashcode和equals方法) // (如果是自定义对象,必须重写hashcode和equals方法) System.out.println("==========4.Stream<T> distinct() :元素去重,依赖(hashcode和equals方法)=========="); System.out.println("元素去重后的元素是:"); //获取Stream流对象 list.stream() //调用Stream流中的distinct()方法 .distinct() //遍历Stream流 .forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.print(s + " "); } }); System.out.println(); //5.static<T> Stream<T> concat(Stream a, Stream b) : 合并a和b两个流为一个流 //注意:如果合并的两个流是同种数据类型,则合并后的流也是该数据类型 // 如果合并的两个流不是同种数据类型,则合并后的流是父类数据类型,且不能使用子类的方法 System.out.println("==========5.static<T> Stream<T>concat(Stream a, Stream b) : 合并a和b两个流为一个流=========="); System.out.println("将list流和list1流合并后的元素为:"); //创建list1集合 ArrayList<String> list1 = new ArrayList<>(); Collections.addAll(list1, "张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤"); //调用Stream流中的concat方法(静态方法直接通过类名调用) Stream.concat(list1.stream(), list.stream()) //遍历Stream流 .forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.print(s + " "); } }); System.out.println(); //6.Stream<R> map(Function<T,R> mapper): 转换流中的数据类型 System.out.println("==========6.Stream<R> map(Function<T,R> mapper): 转换流中的数据类型 =========="); System.out.println("流中的年龄为:"); ArrayList<String> list2 = new ArrayList<>(); Collections.addAll(list2, "张三丰-11", "张无忌-12", "张翠山-12", "王二麻子-23", "张良-14", "谢广坤-15"); //获取Stream流对象 list2.stream() //调用Stream流中的map方法(第一个类型:流中原本的类型;第二个类型:要转成的类型) .map(new Function<String, Integer>() { @Override //apply方法的参数s:依次表示流中的每一个数据 //返回值:表示要转换之后的数据 public Integer apply(String s) { //根据“-”分割字符串 String[] arr = s.split("-"); //获取字符串分割后的年龄 String ageString = arr[1]; //将字符串类型转换成Integer类型 int age = Integer.parseInt(ageString); return age; } }) //遍历Stream流 .forEach(new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.print(integer + " "); } }); System.out.println(); System.out.println("简写方式:"); //简写方式 list2.stream() .map(s -> Integer.parseInt(s.split("-")[1])) .forEach(s -> System.out.print(s + " ")); } }
- 输出结果
-
1.Stream filter(Predicate<? super T> predicate): 用于对流中的数据进行过滤
-
2.Stream limit(long maxSize) : 获取前几个元素
-
3.Stream skip(long n):跳过前几个元素
-
4.Stream distinct() :元素去重,依赖(hashcode和equals方法)
-
5.static Streamconcat(Stream a, Stream b) : 合并a和b两个流为一个流
-
6.Stream map(Function<T,R> mapper): 转换流中的数据类型
-
2.3 终结操作方法
2.3.1 概述
在处理 Java 8+ 中的 Stream 流时,可以通过终端操作方法来触发对流的处理,并生成最终的结果。
终结方法:终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作
2.3.2 方法
方法名 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行遍历操作 |
long count() | 统计 |
toArray() | 收集流中的数据,放到数组中 |
collect(Collector collector) | 收集流中的数据,放到集合中 |
细节:
- toArray方法的参数的作用:负责创建一个指定类型的数组
- toArray方法的返回值:是一个装着流里面所有数据的数组
2.3.3 代码示例
- 代码示例
package text.text02; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.function.Consumer; import java.util.function.IntFunction; /*终结方法:终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作 | 方法名 | 说明 | ----------------------------- | ------------------------ | void forEach(Consumer action) | 对此流的每个元素执行遍历操作 | long count() | 统计 | toArray() | 收集流中的数据,放到数组中 | collect(Collector collector) | 收集流中的数据,放到集合中 细节: · toArray方法的参数的作用:负责创建一个指定类型的数组 · toArray方法的返回值:是一个装着流里面所有数据的数组 */ public class text68 { public static void main(String[] args) { //创建集合并添加数据 ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "吕布", "吕布", "吕布", "吕布", "吕布", "貂蝉", "小乔", "孙策", "大乔", "露娜", "刘备"); System.out.println(); //1.void forEach(Consumer action): 对此流的每个元素执行遍历操作 System.out.println("==========1.void forEach(Consumer action): 对此流的每个元素执行遍历操作=========="); //完整写法 System.out.println("完整写法:"); list.stream() .forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.print(s + " "); } }); System.out.println(); //简单写法 System.out.println("简单写法:"); list.stream() .forEach(s -> System.out.print(s + " ")); System.out.println(); //2.long count() : 统计 System.out.println("==========2.long count() : 统计=========="); //完整写法 System.out.print("完整写法:"); long count = list.stream() .count(); System.out.println(count); //简单写法 System.out.print("简单写法:"); System.out.println(list.stream().count()); //3.toArray() : 收集流中的数据,放到数组中 System.out.println("==========3.toArray() : 收集流中的数据,放到数组中=========="); //参数为空 System.out.print("参数为空:"); Object[] arr1 = list.stream() .toArray(); System.out.println(Arrays.toString(arr1)); //参数不为空 System.out.println("参数不为空:"); //完整形式 System.out.println("完整形式:"); String[] arr2 = list.stream() //IntFunction的泛型:具体类型的数组 //toArray方法的参数的作用:负责创建一个指定类型的数组 //toArray方法的返回值:是一个装着流里面所有数据的数组 .toArray(new IntFunction<String[]>() { @Override //apply的形参:流中数组的个数,要跟数组的长度保持一致 //apply的返回值:集体类型的数组 public String[] apply(int value) { return new String[value]; } }); System.out.println(Arrays.toString(arr2)); //简单形式 System.out.println("简单形式:"); System.out.println(Arrays.toString(list.stream().toArray(value -> new String[value]))); } }
- 输出结果
-
1.void forEach(Consumer action): 对此流的每个元素执行遍历操作
-
2.long count() : 统计
-
3.toArray() : 收集流中的数据,放到数组中
-
2.4 收集操作方法
2.4.1 概述
在 Java 8+ 中,Stream 提供了强大的收集操作方法,可以将流中的元素收集到不同类型的集合中或者生成其他形式的结果。
2.4.2 方法
collect(Collector collector) | 收集流中的数据,放到集合中 (List、Set、Map)
-
toMap:
- 参数一表示键的生成规则
- 参数二表示值的生成规则
-
参数一:
- Function
-
泛型一:表示流中每一个数据的;
-
泛型二:表示Map集合中键的
-
- apply方法
- 形参:依次表示流里面的每一个数
- 方法体:生成键的代码
- 返回值:已经生成的键
- Function
-
参数二:
- Function
-
泛型一:表示流中每一个数据的
-
泛型二:表示Map集合中值的
-
- apply方法
-
形参:依次表示流里面的每一个数
-
方法体:生成值的代码
-
返回值:已经生成的值
-
- Function
注意:如果要收集到Map集合当中,键不能重复,否则会报错
工具类Collectors提供了具体的收集方式:
方法名 | 说明 |
---|---|
public static Collector toList() | 把元素收集到List集合中 |
public static Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper, Function valueMapper) | 把元素收集到Map集合中 |
2.4.3 代码示例
- 代码示例
package text.text02; import java.util.*; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; /*collect(Collector collector) | 收集流中的数据,放到集合中 (List、Set、Map) toMap:参数一表示键的生成规则 参数二表示值的生成规则 参数一: Function泛型一:表示流中每一个数据的 泛型二:表示Map集合中键的 apply方法形参:依次表示流里面的每一个数 方法体:生成键的代码 返回值:已经生成的键 参数二: Function泛型一:表示流中每一个数据的 泛型二:表示Map集合中值的 apply方法形参:依次表示流里面的每一个数 方法体:生成值的代码 返回值:已经生成的值 注意:如果要收集到Map集合当中,键不能重复,否则会报错 工具类Collectors提供了具体的收集方式 | 方法名 | 说明 | | ------------------------------------------------------------ | ---------------------- | | public static <T> Collector toList() | 把元素收集到List集合中 | | public static <T> Collector toSet() | 把元素收集到Set集合中 | | public static Collector toMap(Function keyMapper,Function valueMapper) | 把元素收集到Map集合中 | */ public class text69 { public static void main(String[] args) { //创建集合对象并添加元素 ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "吕布-男-14", "吕布-男-14", "吕布-男-14", "吕布-男-14", "吕布-男-14", "貂蝉-女-13", "小乔-女-16", "孙策-男-17", "大乔-女-17", "露娜-女-15", "刘备-男-18"); //1.把元素收集到List集合中 //需求:将男生收集到List集合 System.out.println("===================1.把元素收集到List集合中==================="); //完整模式 System.out.println("完整模式:"); //创建Stream对象 List<String> collect1 = list.stream() //调用Stream流里的filter()过滤方法 .filter(new Predicate<String>() { @Override public boolean test(String s) { //分割字符串 String[] arr1 = s.split("-"); //获取分割之后的索引为1的字符串 String s1 = arr1[1]; return "男".equals(s1); } }) //调用Stream流中的collect方法 .collect(Collectors.toList()); System.out.println(collect1); //简写模式 System.out.println("简写模式:"); System.out.println(list.stream() .filter(s -> "男".equals(s.split("-")[1])) .collect(Collectors.toList())); //2.把元素收集到Set集合中 //需求:将男生收集到Set集合 System.out.println("===================2.把元素收集到Set集合中==================="); //完整模式 System.out.println("完整模式:"); //创建Stream对象 Set<String> collect2 = list.stream() //调用Stream流里的filter()过滤方法 .filter(new Predicate<String>() { @Override public boolean test(String s) { //分割字符串 String[] arr1 = s.split("-"); //获取分割之后的索引为1的字符串 String s1 = arr1[1]; return "男".equals(s1); } }) //调用Stream流中的collect方法 .collect(Collectors.toSet()); System.out.println(collect2); //简写模式 System.out.println("简写模式:"); System.out.println(list.stream() .filter(s -> "男".equals(s.split("-")[1])) .collect(Collectors.toSet())); //3.把元素收集到Map集合中 //注意:如果要收集到Map集合当中,键不能重复,否则会报错 //创建不重复的集合对象并添加元素 ArrayList<String> list1 = new ArrayList<>(); Collections.addAll(list1, "吕布-男-14", "貂蝉-女-13", "小乔-女-16", "孙策-男-17", "大乔-女-17", "露娜-女-15", "刘备-男-18"); System.out.println("===================3.把元素收集到Map集合中==================="); //完整模式 System.out.println("完整模式:"); Map<String, Integer> collect = list1.stream() //调用Stream里的filter过滤方法 .filter(new Predicate<String>() { @Override public boolean test(String s) { //分割字符串 String[] split = s.split("-"); //获取分割字符串之后的索引为1的字符串 String s1 = split[1]; return "男".equals(s1); } }) /* toMap:参数一表示键的生成规则 参数二表示值的生成规则 参数一: Function泛型一:表示流中每一个数据的类型 泛型二:表示Map集合中键的数据类型 apply方法形参:依次表示流里面的每一个数据 方法体:生成键的代码 返回值:已经生成的键 参数二: Function泛型一:表示流中每一个数据的类型 泛型二:表示Map集合中值的数据类型 apply方法形参:依次表示流里面的每一个数据 方法体:生成值的代码 返回值:已经生成的值 */ .collect(Collectors.toMap( new Function<String, String>() { @Override //生成键 public String apply(String key) { //获取分割字符串之后的索引为0的字符串 String s = key.split("-")[0]; return s; } } , new Function<String, Integer>() { @Override //生成值 public Integer apply(String value) { //获取分割字符串之后的索引为2的字符串 String s = value.split("-")[2]; //将字符串转换成Integer类型 int i = Integer.parseInt(s); return i; } })); System.out.println(collect); //简写模式 System.out.println("简写模式:"); System.out.println(list1.stream() .filter(s -> "男" .equals(s.split("-")[1])) .collect(Collectors.toMap( key -> key.split("-")[0] , value -> Integer.parseInt(value.split("-")[2])))); } }
- 输出结果
-
1.把元素收集到List集合中
-
2.把元素收集到Set集合中
-
3.把元素收集到Map集合中
-
3. 代码示例1
- 代码示例
数据过滤: 定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10;过滤奇数,只留下偶数,并将结果保存起来。package text.text02; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; /* 数据过滤: 定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10;过滤奇数,只留下偶数,并将结果保存起来。 */ public class text70 { public static void main(String[] args) { //定义集合并添加数据 ArrayList<Integer> list = new ArrayList<>(); Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); //完整模式 System.out.println("完整模式:"); List<Integer> collect = list.stream() //过滤 .filter(new Predicate<Integer>() { @Override public boolean test(Integer integer) { //如果判断的结果为true,则当前的数据留下 //如果判断的结果为false,则当前数据就不要. return integer % 2 == 0; } }) //保存数据 .collect(Collectors.toList()); //输出保存的集合 System.out.println(collect); //[2, 4, 6, 8, 10] //省略模式 System.out.println("省略模式:"); System.out.println(list.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList())); } }
- 输出结果
4. 代码示例2
-
代码示例
数据操作:创建一个ArrayList集合,并添加以下字符串,字符串中前面的是姓名,后面是年龄,保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值"zhangsan,23" "lisi,24" "wangwu,25"
package text.text02; 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; /* 数据操作: 创建一个ArrayList集合,并添加以下字符串,字符串中前面的是姓名,后面是年龄 "zhangsan,23" "lisi,24" "wangwu,25" 保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值 */ public class text71 { public static void main(String[] args) { //创建集合,并添加字符串 ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "zhangsan,23", "lisi,24", "wangwu,25"); //完整模式 System.out.println("完整模式:"); Map<String, Integer> map = list.stream() //过滤 .filter(new Predicate<String>() { @Override public boolean test(String s) { String[] split = s.split(","); String s1 = split[1]; int age = Integer.parseInt(s1); 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); //{lisi=24, wangwu=25} //省略模式 System.out.println("省略模式:"); System.out.println(list.stream() .filter(s -> Integer.parseInt(s.split(",")[1]) >= 24) .collect(Collectors.toMap( key -> key.split(",")[0] , value -> Integer.parseInt(value.split(",")[1])))); } }
-
输出结果
5. 代码示例3
-
代码示例
现在有两个ArrayList集合,第一个集合中:存储6名男演员的名字和年龄。第二个集合中,存储6名女演员的名字和年龄,姓名和年龄之间用逗号隔开。比如:张三,23要求完成如下操作:
-
男演员只要名字为3个字的前两人
-
女演员只要姓杨的,并且不要第一个
-
把过滤后的男演员姓名和女演员姓名合并到一起
-
将上一步的演员信息封装成Actor对象
-
将所有的演员对象都保存到List集合中
备注:演员类:Actor,属性:name,age
package text.text02; import java.util.ArrayList; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; /* 现在有两个ArrayList集合,第一个集合中:存储6名男演员的名字和年龄。第二个集合中,存储6名女演员的名字和年龄,姓名和年龄之间用逗号隔开。比如:张三,23 要求完成如下操作: 1.男演员只要名字为3个字的前两人 2.女演员只要姓杨的,并且不要第一个 3.把过滤后的男演员姓名和女演员姓名合并到一起 4.将上一步的演员信息封装成Actor对象 5.将所有的演员对象都保存到List集合中 备注:演员类:Actor,属性:name,age */ public class text72 { public static void main(String[] args) { //创建集合 ArrayList<String> manList = new ArrayList<String>(); manList.add("周润发,23"); manList.add("成龙,24"); manList.add("刘德华,46"); manList.add("吴京,14"); manList.add("周星驰,25"); manList.add("李连杰,54"); ArrayList<String> womanList = new ArrayList<String>(); womanList.add("林心如,24"); womanList.add("张曼玉,35"); womanList.add("杨幂,15"); womanList.add("杨柳岩,23"); womanList.add("林志玲,45"); womanList.add("杨祖贤,23"); //1.男演员只要名字为3个字的前两人 Stream<String> manStream = manList.stream() .filter(s -> s.split(",")[0].length() == 3) .limit(2); //2.女演员只要姓杨的,并且不要第一个 Stream<String> womenStream = womanList.stream() .filter(s -> s.split(",")[0].startsWith("杨")) .skip(1); //3.把过滤后的男演员姓名和女演员姓名合并到一起 Stream<String> stream = Stream.concat(manStream, womenStream); //4.将上一步的演员信息封装成Actor对象 stream.map( new Function<String, Actor>() { @Override public Actor apply(String s) { //获取姓名 String name = s.split(",")[0]; //获取年龄 int age = Integer.parseInt(s.split(",")[1]); //返回对象 return new Actor(name, age); } }) //5.将所有的演员对象都保存到List集合中 .collect(Collectors.toList()) //遍历保存的List集合集合 .forEach(new Consumer<Actor>() { @Override public void accept(Actor actor) { System.out.println(actor.getName() + " , " + actor.getAge()); } }); } } class Actor { private String name; private int age; public Actor() { } public Actor(String name, int age) { this.name = name; this.age = age; } /** * 获取 * * @return name */ public String getName() { return name; } /** * 设置 * * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * * @return age */ public int getAge() { return age; } /** * 设置 * * @param age */ public void setAge(int age) { this.age = age; } public String toString() { return "Actor{name = " + name + ", age = " + age + "}"; } }
-
-
输出结果
6. 注意事项
-
流只能遍历一次:一旦对流进行了终端操作,比如使用了终端操作方法或者转换为了一个集合,流就已经被消耗完毕,不能再进行其他操作。如果需要对流进行多次操作,可以通过创建新的流来实现。
-
及早终止操作:流中定义的操作分为两类:中间操作和终端操作。中间操作像过滤、映射等只返回一个新的流,而终端操作才会触发流的处理。如果不进行终端操作,中间操作不会被执行。
-
流的惰性求值:在执行终端操作之前,中间操作不会立即执行。只有在需要结果时,才会触发中间操作执行。这种延迟求值的特性可以提高性能。
-
避免使用无限流:Stream 提供了生成无限流的方法,如 iterate 和 generate。使用时要特别小心,确保有适当的终止条件,否则会导致无限循环。