文章目录
- 1、体验Stream流
- 2、Stream流的常见生成方式
- 3、Stream流中间操作方法
- 4、Stream流终结操作方法
- 5、Stream流的收集操作
- 6、Stream流综合练习
- 6.1 练习1
- 6.2 练习2
- 6.3 练习3
以下代码使用JDK11编写。
1、体验Stream流
(1)案例需求
按照下面的要求完成集合的创建和遍历
- 创建一个集合,存储多个字符串元素
- 把集合中所有以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
- 遍历上一步得到的集合
(2)原始方式示例代码
public class StreamDemo01 {public static void main(String[] args) {//集合的批量添加ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));//list.add()//遍历list1把以张开头的元素添加到list2中。ArrayList<String> list2 = new ArrayList<>();for (String s : list1) {if(s.startsWith("张")){list2.add(s);}}//遍历list2集合,把其中长度为3的元素,再添加到list3中。ArrayList<String> list3 = new ArrayList<>();for (String s : list2) {if(s.length() == 3){list3.add(s);}}for (String s : list3) {System.out.println(s);} }
}
(3)使用Stream流示例代码
public class StreamDemo01 {public static void main(String[] args) {//集合的批量添加ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));//Stream流list1.stream().filter(s->s.startsWith("张")).filter(s->s.length() == 3).forEach(s-> System.out.println(s));}
}
(4)Stream流的好处
- 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印
- Stream流把真正的函数式编程风格引入到Java中
- 代码简洁
- 结合了Lambda表达式,简化集合、数组的操作
2、Stream流的常见生成方式
(1)Stream流的思想
(2)Stream流的三类方法
- 获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
- 中间方法
- 流水线上的操作
- 一次操作完毕之后,还可以继续进行其他操作
- 终结方法
- 一个Stream流只能有一个终结方法
- 是流水线上的最后一个操作
(3)生成Stream流的方式
-
Collection体系集合:使用默认方法stream()生成流, default Stream stream()
-
Map体系集合:把Map转成Set集合,间接的生成流
-
数组:通过Arrays中的静态方法stream生成流
-
同种数据类型的多个数据:通过Stream接口的静态方法of(T… values)生成流
(4)代码演示
public class StreamDemo {public static void main(String[] args) {//Collection体系的集合可以使用默认方法stream()生成流List<String> list = new ArrayList<String>();Stream<String> listStream = list.stream();Set<String> set = new HashSet<String>();Stream<String> setStream = set.stream();//Map体系的集合间接的生成流Map<String,Integer> map = new HashMap<String, Integer>();Stream<String> keyStream = map.keySet().stream();Stream<Integer> valueStream = map.values().stream();Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();//数组可以通过Arrays中的静态方法stream生成流String[] strArray = {"hello","world","java"};Stream<String> strArrayStream = Arrays.stream(strArray);//同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)生成流Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");Stream<Integer> intStream = Stream.of(10, 20, 30);}
}
(5)详细操作
单列集合(Collection体系集合)
public class StreamDemo02 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"a","b","c","d","e");System.out.println("---------------一条语句:生成流并进行相关操作------------");list.stream().forEach(s -> System.out.println(s));System.out.println("---------------把上面的一条语句进行拆分------------------");// 获取到一条流水线,并把集合中的数据放到流水线上Stream<String> stream1 = list.stream();// 使用终结方法打印一下流水线上的所有数据stream1.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {// s:依次表示流水线上的每一个数据System.out.println(s);}});}
}
双列集合(Map体系的集合间接的生成流)
public class StreamDemo03 {public static void main(String[] args) {// 1.创建双列集合HashMap<String,Integer> hm = new HashMap<>();// 2.添加数据hm.put("aaa",111);hm.put("bbb",222);hm.put("ccc",333);hm.put("ddd",444);// 3.第一种获取stream流,只获取到了keyhm.keySet().stream().forEach(s -> System.out.println(s));// 4.第二种获取stream流,可以获取到key和valuehm.entrySet().stream().forEach(s-> System.out.println(s));}
}
数组(数组可以通过Arrays中的静态方法stream生成流)
public class StreamDemo04 {public static void main(String[] args) {// 1.创建数组int[] arr1 = {1,2,3,4,5,6,7,8,9,10};String[] arr2 = {"a","b","c"};// 2.获取stream流Arrays.stream(arr1).forEach(s-> System.out.println(s));System.out.println("--------------------------------");Arrays.stream(arr2).forEach(s-> System.out.println(s));System.out.println("--------------------------------");/*** 注意:* Stream接口中静态方法of的细节:* 方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组* 但是数组必须是引用数据类型的,如果传递基本数据类型,是会把整个数组当做一个元素,放到Stream当中。*/Stream.of(arr1).forEach(s-> System.out.println(s)); // 输出[I@17f6480}
}
一堆零散数据(同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)
生成流)
public class StreamDemo05 {public static void main(String[] args) {// 基本类型Stream.of(1,2,3,4,5).forEach(s-> System.out.println(s));// 引用类型Stream.of("a","b","c","d","e").forEach(s-> System.out.println(s));}
}
3、Stream流中间操作方法
注意1:如果是直接操作Stream流(如Stream.concat(stream1, stream2),而不是使用集合的stream格式,如list.stream()),中间方法返回的是新的Stream流,原来的Stream流只能使用一次。例如,用filter、limit、skip、concat、distinct、map等中间操作后,stream对象就无法再次被操作。建议使用链式编程或者把中间操作的结果存储到新的stream变量中再使用。
注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据
(1)概念:中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作
(2)常见方法
方法名 | 说明 |
---|---|
Stream filter(Predicate predicate) | 用于对流中的数据进行过滤 |
Stream limit(long maxSize) | 返回此流中的元素组成的流,截取前指定参数个数的数据 |
Stream skip(long n) | 跳过指定参数个数的数据,返回由该流的剩余元素组成的流 |
static Stream concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
Stream distinct() | 返回由该流的不同元素(根据Object.equals(Object) )组成的流 |
Stream map(Function<T,R> mapper) | 转换流中的数据类型 |
(3)filter代码演示
public class StreamDemo06 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");// 1、把张开头且字符串长度为3的留下,其余数据过滤不要System.out.println("--------filter过滤:匿名内部类格式---------");list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s) {//如果返回值为true,表示当前数据留下//如果返回值为false,表示当前数据舍弃不要return s.startsWith("张")&&s.length()==3;}}).forEach(s -> System.out.println(s));System.out.println("--------filter过滤:Lambda表达式格式---------");list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));System.out.println("--------filter过滤:Lambda表达式格式---------");}
}
(4)limit&skip代码演示
public class StreamDemo06 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");System.out.println("----limit获取前几个元素----");list.stream().limit(3).forEach(s -> System.out.println(s));System.out.println("----skip跳过前几个元素----");list.stream().skip(4) .forEach(s -> System.out.println(s));System.out.println("----练习:思路1----");/*** 课堂练习:获取 "张强", "张三丰", "张翠山"** 第一种思路:先获取前面6个元素:"张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山",* 然后跳过前面3个元素*/list.stream().limit(6).skip(3).forEach(s -> System.out.println(s));/*** 第二种思路:* 先跳过3个元素:"张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤"* 然后再获取前面3个元素:"张强", "张三丰", "张翠山"*/System.out.println("----练习:思路2----");list.stream().skip(3).limit(3).forEach(s -> System.out.println(s));}
}
(5)concat&distinct代码演示
public class StreamDemo07 {public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1, "张无忌","张无忌","张无忌", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");ArrayList<String> list2 = new ArrayList<>();Collections.addAll(list2, "周芷若", "赵敏");System.out.println("----------------distinct元素去重---------");list1.stream().distinct().forEach(s -> System.out.println(s));System.out.println("----------------concat合并a和b两个流为一个流---------");Stream.concat(list1.stream(),list2.stream()).forEach(s -> System.out.println(s));}
}
(6)map代码演示
public class StreamDemo08 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌-15", "周芷若-14", "赵敏-13", "张强-20", "张三丰-100", "张翠山-40", "张良-35", "王二麻子-37", "谢广坤-41");//需求:获取里面的年龄并进行打印System.out.println("--------map转换流中的数据类型:匿名内部类格式---------");/*** 第一个类型:流中原本的数据类型* 第二个类型:要转成之后的类型* apply的形参s:依次表示流里面的每一个数据,其返回值表示转换之后的数据* 当map方法执行完毕之后,流上的数据就变成了整数* 所以在下面forEach当中,s依次表示流里面的每一个数据,这个数据现在就是整数了*/list.stream().map(new Function<String, Integer>() {@Overridepublic Integer apply(String s) {String[] arr = s.split("-");String ageString = arr[1];int age = Integer.parseInt(ageString);return age;}}).forEach(s-> System.out.println(s));System.out.println("--------map转换流中的数据类型:Lambda表达式格式---------");list.stream().map(s ->Integer.parseInt(s.split("-")[1])).forEach(s-> System.out.println(s));}
}
4、Stream流终结操作方法
(1)概念:终结操作的意思是执行完此方法之后,Stream流将不能再执行其他操作
(2)常见方法
方法名 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行操作 |
long count() | 返回此流中的元素数 |
toArray() | 收集流中的数据,放到数组中 |
R collect(Collector collector) | 收集流中的数据,放到集合中 |
(3)代码演示
forEach
public class StreamDemo09 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");/*** void forEach(Consumer action) 遍历* Consumer的泛型:表示流中数据的类型* accept方法的形参s:依次表示流里面的每一个数据* 方法体:对每一个数据的处理操作(打印)*/System.out.println("---------forEach:匿名内部类方式-----------");list.stream().forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}});System.out.println("---------forEach:Lambda表达式方式-----------");list.stream().forEach(s -> System.out.println(s));}
}
count
public class StreamDemo09 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");// long count()统计System.out.println("---------count()统计-----------");long count = list.stream().count();System.out.println(count);}
}
5、Stream流的收集操作
(1)概念:对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中
(2)常用方法
方法名 | 说明 |
---|---|
toArray() | 收集流中的数据,放到数组中 |
R collect(Collector collector) | 把结果收集到集合中 |
(3)工具类Collectors提供了具体的收集方式
方法名 | 说明 |
---|---|
public static Collector toList() | 把元素收集到List集合中 |
public static Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper,Function valueMapper) | 把元素收集到Map集合中 |
(4)收集到数组中
public class StreamDemo10 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");// 需求:收集流中的数据,放到数组中-toArray()System.out.println("---------toArray():匿名内部类方式-----------");/*** IntFunction的泛型:具体类型的数组* apply的形参:流中数据的个数,要跟数组的长度保持一致* apply的返回值:具体类型的数组* 方法体:就是创建数组** toArray方法的参数的作用:负责创建一个指定类型的数组* toArray方法的底层,会依次得到流里面的每一个数据,并把数据放到数组当中* toArray方法的返回值:是一个装着流里面所有数据的数组*/String[] arr = list.stream().toArray(new IntFunction<String[]>() {@Overridepublic String[] apply(int value) {return new String[value];}});System.out.println(Arrays.toString(arr));System.out.println("---------toArray():Lambda表达式方式-----------");String[] arr2 = list.stream().toArray(value -> new String[value]);System.out.println(Arrays.toString(arr2));}
}
(5)收集到集合中
注:如果要收集到Map集合当中,键不能重复,否则会报错
package com.ya.mystream;import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;/*** Stream流的收集操作:collect(Collector collector) 收集流中的数据,放到集合中 (List Set Map)*/
public class StreamDemo11 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20","张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");System.out.println("--------收集List集合当中-----------");//把所有的男性收集起来,放到List集合当中List<String> newList1 = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toList());System.out.println(newList1);System.out.println("--------收集Set集合当中-----------");//把所有的男性收集起来,放到Set集合当中Set<String> newList2 = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toSet());System.out.println(newList2);System.out.println("--------收集Map集合当中:匿名内部类方式-----------");/*** 把所有的男性收集起来,放到Map集合当中* 键:姓名。 值:年龄*/Map<String, Integer> map = list.stream().filter(s -> "男".equals(s.split("-")[1]))/** toMap : 参数一表示键的生成规则* 参数二表示值的生成规则** 参数一:* Function泛型一:表示流中每一个数据的类型* 泛型二:表示Map集合中键的数据类型** 方法apply形参:依次表示流里面的每一个数据* 方法体:生成键的代码* 返回值:已经生成的键*** 参数二:* Function泛型一:表示流中每一个数据的类型* 泛型二:表示Map集合中值的数据类型** 方法apply形参:依次表示流里面的每一个数据* 方法体:生成值的代码* 返回值:已经生成的值** */.collect(Collectors.toMap(// keynew Function<String, String>() {@Overridepublic String apply(String s) {//张无忌-男-15return s.split("-")[0];}},// valuenew Function<String, Integer>() {@Overridepublic Integer apply(String s) {return Integer.parseInt(s.split("-")[2]);}}));System.out.println(map);System.out.println("--------收集Map集合当中:Lambda表达式方式-----------");Map<String, Integer> map2 = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toMap(// keys -> s.split("-")[0],// values -> Integer.parseInt(s.split("-")[2])));System.out.println(map2);}
}
6、Stream流综合练习
6.1 练习1
- 案例需求
- 定义一个集合,并添加一些整数 1,2,3,4,5,6,7,8,9,10
- 过滤奇数,只留下偶数
- 并将结果保存起来
public class Test1 {public static void main(String[] args) {//1. 定义一个集合ArrayList<Integer> list = new ArrayList<>();//2.添加一些整数Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);//3.过滤奇数,只留下偶数//进行判断,如果是偶数,返回true 保留List<Integer> newList = list.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());//4.打印集合System.out.println(newList);}
}
6.2 练习2
- 案例需求
- 创建一个ArrayList集合,并添加以下字符串,字符串中前面是姓名,后面是年龄
“zhangsan,23”
“lisi,24”
“wangwu,25” - 保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值。
- 创建一个ArrayList集合,并添加以下字符串,字符串中前面是姓名,后面是年龄
public class Test2 {public static void main(String[] args) {//1.创建一个ArrayList集合ArrayList<String> list = new ArrayList<>();//2.添加以下字符串list.add("zhangsan,23");list.add("lisi,24");list.add("wangwu,25");//3.保留年龄大于等于24岁的人System.out.println("--------收集为Map集合:匿名内部类方式--------");Map<String, Integer> map = list.stream().filter(s -> Integer.parseInt(s.split(",")[1]) >= 24).collect(Collectors.toMap(// keynew Function<String, String>() {@Overridepublic String apply(String s) {return s.split(",")[0];}},// valuenew Function<String, Integer>() {@Overridepublic Integer apply(String s) {return Integer.parseInt(s.split(",")[1]);}}));System.out.println(map);System.out.println("--------收集为Map集合:Lambda表达式方式--------");Map<String, Integer> map1 = list.stream().filter(s -> Integer.parseInt(s.split(",")[1]) >= 24).collect(Collectors.toMap(// keys -> s.split(",")[0],// values -> Integer.parseInt(s.split(",")[1])));System.out.println(map1);}
}
6.3 练习3
-
案例需求
现在有两个ArrayList集合,分别存储6名男演员的名字和年龄以及6名女演员的名字和年龄。 姓名和年龄中间用逗号隔开。 比如:张三,23 要求完成如下的操作: 1,男演员只要名字为3个字的前两人 2,女演员只要姓杨的,并且不要第一个 3,把过滤后的男演员姓名和女演员姓名合并到一起 4,将上一步的演员信息封装成Actor对象。 5,将所有的演员对象都保存到List集合中。 备注:演员类Actor,属性有:name,age 男演员: "蔡坤坤,24" , "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27" 女演员: "赵小颖,35" , "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33"
-
代码实现
演员类
public 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 + "}";} }
测试类
public class Test3 {public static void main(String[] args) {//1.创建两个ArrayList集合ArrayList<String> manList = new ArrayList<>();ArrayList<String> womenList = new ArrayList<>();//2.添加数据Collections.addAll(manList, "蔡坤坤,24", "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27");Collections.addAll(womenList, "赵小颖,35", "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33");//3.男演员只要名字为3个字的前两人Stream<String> stream1 = manList.stream().filter(s -> s.split(",")[0].length() == 3).limit(2);//4.女演员只要姓杨的,并且不要第一个Stream<String> stream2 = womenList.stream().filter(s -> s.split(",")[0].startsWith("杨")).skip(1);//5.把过滤后的男演员姓名和女演员姓名合并到一起//演员信息封装成Actor对象,String -> Actor对象 (类型转换,使用map中间操作方法)// 注意:这里只能使用一个方式,两个方式同时打开运行会报错“IllegalStateException: stream has already been operated upon or closed”// 因为stream1, stream2已经被操作过了,不是原来的stream1, stream2了/*System.out.println("-------------匿名内部类方式--------------");List<Actor> list = Stream.concat(stream1, stream2).map(new Function<String, Actor>() {@Overridepublic Actor apply(String s) {String name = s.split(",")[0];int age = Integer.parseInt(s.split(",")[1]);return new Actor(name, age);}}).collect(Collectors.toList());System.out.println(list);*/System.out.println("-------------Lambda表达式方式--------------");List<Actor> list1 = Stream.concat(stream1, stream2).map(s -> new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1]))).collect(Collectors.toList());System.out.println(list1);} }