Stream介绍和实战
1. Stream简介
1.1 官方说明
Stream官方地址,简单说Stream可以对集合进行各种非常高效的操作,配合上Lambda表达式,让代码更简洁。
1.2 语法
例如:
int sum = widgets.stream()
.filter(b -> b.getColor() == RED)
.mapToInt(b -> b.getWeight())
.sum();
我们使用widgets,一个Collection<Widget>,作为流的源,并在流上执行过滤-映射-归约操作,以获得红色小部件的重量总和。(求和是归约操作的一个示例。)
1.3 特点
该包引入的关键抽象是流(stream)。Stream、IntStream、LongStream 和 DoubleStream 这些类是对象和原始 int、long 和 double 类型的流。流与集合在几个方面有所不同:
1. 无存储。流不是存储元素的数据结构;相反,它通过计算操作的管道,从数据结构、数组、生成器函数或 I/O 通道等源传递元素。
2. 函数式特性。对流的操作会产生结果,但不会修改其源。例如,对从集合获取的流进行过滤会生成一个新的流,而不是从源集合中删除元素。
3. 寻求惰性。许多流操作,如过滤、映射或去重,可以实现惰性执行,从而提供优化机会。例如,“找到具有三个连续元音字母的第一个字符串”不需要检查所有输入字符串。流操作分为中间(生成流)操作和终端(产生值或副作用)操作。中间操作总是惰性执行。
4. 可能无界。集合具有有限大小,而流则不一定。诸如 limit(n) 或 findFirst() 之类的短路操作可以使对无限流的计算在有限时间内完成。
5. 可消费。流的元素在流的生命周期内只被访问一次。与迭代器类似,要重新访问源的相同元素,必须生成一个新的流。
2. 组成
一个stream操作主要有3个部分:源头、中间操作、终止操作。
中间操作可以有0个或多个,但不是立马执行的,只有终止操作被调用后中间操作才会一起执行(惰性执行)
2.1 源头
一般有
数组
int[] demoArr = new int[]{2,4,6,1};
Arrays.stream(demoArr);
Stream.of(demoArr);
集合Collection
List<Object> demoList = new ArrayList<>();
demoList.stream();
Stream.of(demoList);;
2.2 中间操作
一般有
filter 条件过滤
map 对流中的每个元素按照指定的映射规则进行转换
flatmap 将每个元素映射成一个流,然后将所有流合并成一个流的方法。这个方法通常用于将嵌套的集合展开成一个扁平的流。
sorted 按照规则排序
skip 跳过指定数量的元素,返回一个包含剩余元素的新流
limit 数量限制
distinct 去重
2.3 终止操作
一般有
reduce
foreach
collect
allMatch
anyMatch
noneMatch
findFirst
findAny
max
min
count
3. 案例
3.1 中间操作案例
通用参数
List<User> userList = new ArrayList<>();
User userYang = new User("1", "小杨", 18);
userList.add(userYang);
User userWang = new User("2", "小王", 16);
userList.add(userWang);
User userLi = new User("3", "小李", 20);
userList.add(userLi);
List<String> list1 = Arrays.asList("hello", "world");
List<String> list2 = Arrays.asList("java", "stream");
1. filter过滤
userList.stream()
.filter(item -> item.getCode().equals("1"))
.forEach(item -> {
System.out.println(item.getName());
});
输出: 小杨
2.1. map用法1,提取code
List<String> codeList = userList.stream().map(User::getCode).collect(Collectors.toList());
System.out.println(JSON.toJSONString(codeList));
输出: ["1","2","3"]
2.2. map用法2,便利修改当前对象,不产生新对象
userList.stream().map(item -> {
item.setAge(18);
return item;
}).collect(Collectors.toList());
System.out.println(JSON.toJSONString(userList));
输出: [{"age":18,"code":"1","name":"小杨"},{"age":18,"code":"2","name":"小王"},{"age":18,"code":"3","name":"小李"}]
3. flatMap,将一个Stream中的每个元素转换为另一个Stream,然后将所有转换后的Stream合并为一个新的Stream
List<String> result = Stream.of(list1, list2)
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println(result);
输出: [hello, world, java, stream]
4. sort
List<User> sortUserList = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
System.out.println(JSON.toJSONString(sortUserList));
输出: [{"age":16,"code":"2","name":"小王"},{"age":18,"code":"1","name":"小杨"},{"age":20,"code":"3","name":"小李"}]
5. skip
List<User> skipUserList = userList.stream().skip(1).collect(Collectors.toList());
System.out.println(JSON.toJSONString(skipUserList));
输出: [{"age":16,"code":"2","name":"小王"},{"age":20,"code":"3","name":"小李"}]
6. limit
List<User> limitUserList = userList.stream().limit(1).collect(Collectors.toList());
System.out.println(JSON.toJSONString(limitUserList));
输出: [{"age":18,"code":"1","name":"小杨"}]
7. distinct
User userLiCopy = new User("3", "小李", 20);
userList.add(userLiCopy);
List<User> distinctUserList = userList.stream().distinct().toList();
System.out.println(JSON.toJSONString(distinctUserList));
输出: 添加一个重复元素之后去重, [{"age":18,"code":"1","name":"小杨"},{"age":16,"code":"2","name":"小王"},{"age":20,"code":"3","name":"小李"}]
3.2 终止操作案例
1. reduce:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, (acc, x) -> acc + x);
System.out.println("Sum of numbers: " + sum);
输出: Sum of numbers: 15
2. foreach:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);
输出:
Alice
Bob
Charlie
3. collect:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers.stream().map(x -> x * x).collect(Collectors.toList());
System.out.println("Squares of numbers: " + squares);
输出: Squares of numbers: [1, 4, 9, 16, 25]
4. allMatch:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allEven = numbers.stream().allMatch(x -> x % 2 == 0);
System.out.println("Are all numbers even? " + allEven);
输出: Are all numbers even? false
5. anyMatch:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean anyEven = numbers.stream().anyMatch(x -> x % 2 == 0);
System.out.println("Are there any even numbers? " + anyEven);
输出: Are there any even numbers? true
6. noneMatch:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean noneNegative = numbers.stream().noneMatch(x -> x < 0);
System.out.println(noneNegative);
输出: true
7. findFirst:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Optional<String> first = names.stream().findFirst();
System.out.println("First name: " + first.orElse("None"));
输出: First name: Alice
8. findAny:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Optional<String> any = names.stream().findAny();
System.out.println("Any name: " + any.orElse("None"));
输出: Any name: Alice
9. max:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> maxNum = numbers.stream().max(Integer::compare);
System.out.println("Max number: " + maxNum.orElse(0));
输出: Max number: 5
10. min:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> minNum = numbers.stream().min(Integer::compare);
System.out.println("Min number: " + minNum.orElse(0));
输出: Min number: 1
11. count:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream().count();
System.out.println("Number of elements: " + count);
输出:Number of elements: 5