# Stream 流式编程
# 分类
中间操作可分为:
- 无状态:指元素的处理不受之前元素的影响
- 有状态:指该操作只有拿到所有元素之后才能继续下去
终结操作可分为:
- 非短路操作:指必须处理所有元素才能得到最终结果
- 短路操作:指遇到某些符合条件的元素就可以得到最终结果
具体操作如图所示:
# Stream API 使用
# 流的构成
使用流的时候,通常包括三个步骤:
- 获取一个数据源
- 数据转换
执行操作获取想要的结果,每次转换原有的 Stream 对象不变,返回一个新的 Stream 对象,这就允许对其操作可以像链条一样排列
# 流的创建
-
通过 java.util.Collection.stream () 方法用集合创建流
List<String> list = Arrays.asList("hello","world","stream"); //创建顺序流 Stream<String> stream = list.stream(); //创建并行流 Stream<String> parallelStream = list.parallelStream();
-
使用 java.util.Arrays.stream (T [] array) 方法用数组创建流
String[] array = {"h", "e", "l", "l", "o"}; Stream<String> arrayStream = Arrays.stream(array);
-
Stream 的静态方法:of ()、iterate ()、generate ()
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6); Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(3); Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
-
stream 和 parallelStream 的简单区分
stream 是顺序流,由主线程按顺序对流执行操作,而 parallelStream 是并行流,内部以多线程并行执行的方式对流进行操作,需要注意使用并行流的前提是流中的数据处理没有顺序要求(会乱序,即使用了 forEachOrdered)
# 一、Stream 中间操作
Stream流中间操作是指在流链当中,可以对数据进行处理操作,包括filter过滤、map映射转换、flatMap合并、distinct去重、sorted排序等操作,这些操作都会返回一个新的Stream流对象,可以通过链式调用多个中间操作进行复杂的数据处理。
需要注意的是,中间操作需要具有终止操作才会触发。
# 无状态 (Stateless) 操作
# filter: 过滤出符合条件的元素
filter () 方法常用于实现数据过滤,即可以对集合、数组等数据源筛选出符合指定条件的元素,并返回一个新的流
// 将数组转换为一个字符串列表
List<String> numbers = Arrays.asList"13378520000","13278520000","13178520000","13358520000");
// 通过stream()方法创建一个流,接着使用filter方法过滤出前缀为"133"的元素,最终通过collect()方法将结果收集到一个新的列表中
List<String> filterNumbers = numbers.stream().filter(s -> s.startWith("133")).collect(Collectors.toList());
System.out.println(filterNumbers);
打印结果:[13378520000, 13358520000]
# map: 映射转换元素
map 方法用于对流中的每个元素进行映射操作,将其转换为另一个元素或者提取其中的信息,并返回一个新的流
1.2.1 转换元素
获取每个手机号的前七位子字符串
List<String> numbers = Arrays.asList("13378520000","13278520000","13178520000","13558520000");
//通过stream()方法创建一个流,使用map()方法将每个字符串转换为截取前7位的字符,最后使用collect()方法将结果收集到一个新列表中
List<String> filterdNumbers = numbers.stream().map(s -> s.substring(0,7)).collect(Collectors.toList());
System.out.println(filterdNumbers);
打印结果:[1337852, 1327852, 1317852, 1355852]
1.2.2 提取元素信息
获取每个对象的手机号
List<People> peopleList = Arrays.asList(
new People("王二","13378520000"),
new People("李二","13278520000"),
new People("张四","13178520000")
);
//通过stream()方法创建一个流,使用map()方法提取每个用户的手机号,最后使用collect()方法将结果收集到一个新列表中
List<String> tel = peopleList.stream().map(People::getTel).collect(Collectors.toList());
System.out.println(tel);
打印结果:[13378520000, 13278520000, 13178520000]
# flatMap: 将多个流合并为一个流
1.3.1 实现多对多的映射
将 A 组数据和 B 组数据一次进行相加
List<Integer> listA = Arrays.asList(1,2,3);
List<Integer> listB = Arrays.asList(4, 5, 6);
List<Integer> list = listA.stream().flatMap(a -> listB.stream().map(b -> a +b)).collect(Collectors.toList());
System.out.println(list);
打印结果: [5, 6, 7, 6, 7, 8, 7, 8, 9]
1.3.2 将多个列表合并成一个列表
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("13378520000", "13278520000"),
Arrays.asList("13178520000", "13558520000"),
Arrays.asList("15138510000", "15228310000")
);
List<String> flatMapList = listOfLists.stream().flatMap(Collection::stream).collect(Collectors.toList());
System.out.println(flatMapList);
打印结果:[13378520000, 13278520000, 13178520000, 13558520000, 15138510000, 15228310000]
# peek: 查看每个元素的信息,但不改变流中元素的状态
List<String> telList = Arrays.asList("13378520000","13278520000","13178520000","13558520000");
telList.stream().peek(t -> System.out.println(t))
.map(t -> t.substring(0,3))
.peek(t -> System.out.println(t))
.collect(Collectors.toList());
重点: peek VS map
peek 操作一般用于 不想改变流中元素本身的类型或者只想元素的内部状态改变时
map 则用于改变流中元素本身,即从元素中派生出另一种类型的操作
# mapToInt、mapToLong、mapToDouble、flatMapToDouble、flatMapToInt、flatMapToLong
以上操作是 map 和 flatMap 的特别版,也就是针对特定的数据类型进行映射处理
其对应方法如下:
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
# 有状态 (Stateful) 操作
# distinct: 去除重复的元素
distinct () 方法可以用来去除流中的重复元素,生成无重复的列表
需要注意的是:distinct 用于针对流元素去重操作时,需要确定流中的元素实现了 equals () 和 hasCode () 方法
List<String> numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000");
List<String> disNumbers = numbers.stream().distinct().collect(Collectors.toList());
System.out.println(disNumbers);
打印结果:[13378520000, 15138510000, 13178520000]
# 1.5 sorted: 排序元素
sorted 方法用于对流中元素进行排序
1.5.1 升序排序
List<People> peopleList = Arrays.asList(
new People("王二",20),
new People("李二",30),
new People("张四",31)
);
List<People> newpeopleList=peopleList.stream().sorted(Comparator.comparing(People::getAge)).collect(Collectors.toList());
//打印结果
newpeopleList.stream().forEach(System.out::println);
打印结果:
People{name='王二', age=20}
People{name='李二', age=30}
People{name='张四', age=31}
1.5.2 降序排序
通过 reversed () 方法进行逆序排序
List<People> peopleList = Arrays.asList(
new People("王二",20),
new People("李二",30),
new People("张四",31)
);
List<People> newpeopleList = peopleList.stream().sorted(Comparator.comparing(People::getAge).reversed()).collect(Collectors.toList());
//打印结果
newpeopleList.stream().forEach(System.out::println);
打印结果:
People{name='张四', age=31}
People{name='李二', age=30}
People{name='王二', age=20}
# 1.7 limit 和 skip: 截取流中的部分元素
limit 返回一个包含前 n 个元素的新流
skip 返回一个丢弃前 n 个元素后剩余元素组成的新流
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.print("取数组前5个元素:");
Arrays.stream(arr).limit(5).forEach(n -> System.out.print(n + " ")); // 输出结果为:1 2 3 4 5
System.out.print("跳过前3个元素,取剩余数组元素:");
Arrays.stream(arr).skip(3).forEach(n -> System.out.print(n + " ")); // 输出结果为:4 5 6 7 8 9 10
# 终结操作 (Terminal Operation)
# 短路 (Short-circuiting) 操作
- anyMatch: Stream 中只要有一个元素符合传入的 predicate, 返回 true
boolean anyMatch(Predicate<? super T> predicate);
- allMatch: Stream 中全部元素符合传入的 predicate, 返回 true
boolean allMatch(Predicate<? super T> predicate);
- noneMatch: Stream 中没有一个元素符合传入的 predicate, 返回 true
boolean noneMatch(Predicate<? super T> predicate);
- findFirst: 用于返回满足条件的第一个元素 (凡是该元素是封装在 Optional 类中)
Optional
findFirst();
- findAny: 返回流中的任意元素 (但是该元素也是封装在 Optional 类中)
Optional
findAny();
# 非短路操作
# forEach()
该方法接收一个 lambda 表达式,然后在 Stream 的每一个元素上执行该表达式
void forEach(Consumer<? super T> action);
# forEachOrdered()
该方法接收一个 Lambda 表达式,然后按顺序在 Stream 中的每一个元素上执行该表达式
void forEachOrdered(Consumer<? super T> action);
该功能其实和 forEach 是很相似的,也是循环操作!那唯一的区别,就在于 forEachOrdered 是可以保证循环时元素是按原来的顺序逐个循环的!
# toArray()
返回包含此流元素的数组,当有参数时,
Object [] toArray();
举例:
List<String> strList = Arrays.asList( "Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
Object [] strAryNoArg = strList.stream().toArray();
String [] strAry = strList.stream().toArray(String[]::new);