文章目录
- 1、体验Stream流
- 2、Stream流的常见生成方式
- 3、Stream流中间操作方法
- 4、Stream流终结操作方法
- 5、Stream流的收集操作
- 6、Stream流综合练习
-
- [6.1 练习1](#6.1 练习1)
- [6.2 练习2](#6.2 练习2)
- [6.3 练习3](#6.3 练习3)
以下代码使用JDK11编写。
1、体验Stream流
(1)案例需求
按照下面的要求完成集合的创建和遍历
- 创建一个集合,存储多个字符串元素
- 把集合中所有以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
- 遍历上一步得到的集合
(2)原始方式示例代码
java
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流示例代码
java
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)代码演示
java
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体系集合)
java
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>() {
@Override
public void accept(String s) {
// s:依次表示流水线上的每一个数据
System.out.println(s);
}
});
}
}
双列集合(Map体系的集合间接的生成流)
java
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流,只获取到了key
hm.keySet().stream().forEach(s -> System.out.println(s));
// 4.第二种获取stream流,可以获取到key和value
hm.entrySet().stream().forEach(s-> System.out.println(s));
}
}
数组(数组可以通过Arrays中的静态方法stream生成流)
java
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)
生成流)
java
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代码演示
java
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>() {
@Override
public 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代码演示
java
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代码演示
java
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代码演示
java
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>() {
@Override
public 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
java
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>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("---------forEach:Lambda表达式方式-----------");
list.stream().forEach(s -> System.out.println(s));
}
}
count
java
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)收集到数组中
java
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[]>() {
@Override
public 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集合当中,键不能重复,否则会报错
java
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(
// key
new Function<String, String>() {
@Override
public String apply(String s) {
//张无忌-男-15
return s.split("-")[0];
}
},
// value
new Function<String, Integer>() {
@Override
public 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(
// key
s -> s.split("-")[0],
// value
s -> Integer.parseInt(s.split("-")[2])));
System.out.println(map2);
}
}
6、Stream流综合练习
6.1 练习1
- 案例需求
- 定义一个集合,并添加一些整数 1,2,3,4,5,6,7,8,9,10
- 过滤奇数,只留下偶数
- 并将结果保存起来
java
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集合,并添加以下字符串,字符串中前面是姓名,后面是年龄
java
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(
// key
new Function<String, String>() {
@Override
public String apply(String s) {
return s.split(",")[0];
}
},
// value
new Function<String, Integer>() {
@Override
public 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(
// key
s -> s.split(",")[0],
// value
s -> 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"
-
代码实现
演员类
javapublic 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 + "}"; } }
测试类
javapublic 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>() { @Override public 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); } }