1. Stream概述
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
2. 什么是Stream ?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
3. Stream操作分类
- 无状态:指元素的处理不受之前元素的影响;
- 有状态:指该操作只有拿到所有元素之后才能继续下去。
- 非短路操作:指必须处理所有元素才能得到最终结果;
- 短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。
4. Stream常用操作
1. 排序
- List
# 数字排序
List<Integer> list = Arrays.asList(3, 2, 6, 5, 7, 1);
//升序:1, 2, 3, 5, 6, 7
List<Integer> asc = list.stream().sorted().collect(Collectors.toList());
//降序:7, 6, 5, 3, 2, 1
List<Integer> desc = list.stream().sorted((x, y) -> y - x).collect(Collectors.toList());
# 字符串排序
List<String> list = Arrays.asList("a", "b", "c", "aa", "ab", "bb", "bc", "ac", "ca");
//升序:a, aa, ab, ac, b, bb, bc, c, ca
List<String> asc = list.stream().sorted().collect(Collectors.toList());
//降序:ca, c, bc, bb, b, ac, ab, aa, a
List<String> desc = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
//反转集合:ca, ac, bc, bb, ab, aa, c, b, a
Collections.reverse(list);
# 对象排序
List<User> list = Arrays.asList(
new User(1, "A", "2022/03/26 10:23:21"),
new User(2, "B", "2022/03/26 10:35:40"),
new User(3, "C", "2022/03/26 09:26:38"),
new User(4, "D", "2022/03/26 11:26:26"),
new User(5, "E", "2022/03/26 10:26:45"));
//根据user.createTime升序:[User{id=3, name='C', createTime=Sat Mar 26 09:26:38 CST 2022}, User{id=1, name='A', createTime=Sat Mar 26 10:23:21 CST 2022}, User{id=5, name='E', createTime=Sat Mar 26 10:26:45 CST 2022}, User{id=2, name='B', createTime=Sat Mar 26 10:35:40 CST 2022}, User{id=4, name='D', createTime=Sat Mar 26 11:26:26 CST 2022}]
List<User> asc = list.stream().sorted(Comparator.comparing(User::getCreateTime)).collect(Collectors.toList());
//根据user.createTime降序:[User{id=4, name='D', createTime=Sat Mar 26 11:26:26 CST 2022}, User{id=2, name='B', createTime=Sat Mar 26 10:35:40 CST 2022}, User{id=5, name='E', createTime=Sat Mar 26 10:26:45 CST 2022}, User{id=1, name='A', createTime=Sat Mar 26 10:23:21 CST 2022}, User{id=3, name='C', createTime=Sat Mar 26 09:26:38 CST 2022}]
List<User> desc = list.stream().sorted(Comparator.comparing(User::getCreateTime).reversed()).collect(Collectors.toList());
//NullPointerException
List<User> list = Arrays.asList(
new User(1, "A", "2022/03/26 10:23:21"),
new User(2, "B", null),
new User(3, "C", "2022/03/26 09:26:38"),
new User(4, "D", "2022/03/26 11:26:26"),
new User(5, "E", "2022/03/26 10:26:45"));
//如果待排序的数据中有一些是空值null,则上述排序方法会出现NPE异常。解决方法是构造Comparator时使用nullsLast明确空值处理方式,代码如下:
Comparator.comparing(User::getCreateTime, Comparator.nullsLast(Date::compareTo))
- Map
//HashMap是无序的,当我们希望有顺序地去存储key-value时,需要使用LinkedHashMap
Map<String, String> map = new HashMap();
map.put("a", "1");
map.put("b", "3");
map.put("d", "4");
map.put("c", "2");
//按照key升序排列:{a=1, b=3, c=2, d=4}
LinkedHashMap<String, String> asc1 = new LinkedHashMap<>();
LinkedHashMap<String, String> asc2 = new LinkedHashMap<>();
map.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey())).forEach(e -> asc1.put(e.getKey(), e.getValue()));
map.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(f -> asc2.put(f.getKey(), f.getValue()));
// Comparator.comparing(e -> e.getKey()) 等价于 Map.Entry.comparingByKey()
//按照key降序排列:{d=4, c=2, b=3, a=1}
LinkedHashMap<String, String> desc1 = new LinkedHashMap<>();
LinkedHashMap<String, String> desc2 = new LinkedHashMap<>();
map.entrySet().stream().sorted(Collections.reverseOrder(Comparator.comparing(e -> e.getKey()))).forEach(f -> desc1.put(f.getKey(), f.getValue()));
map.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByKey())).forEach(f -> desc2.put(f.getKey(), f.getValue()));
//按照value升序排列:{a=1, c=2, b=3, d=4}
LinkedHashMap<String, String> asc3 = new LinkedHashMap<>();
LinkedHashMap<String, String> asc4 = new LinkedHashMap<>();
map.entrySet().stream().sorted(Comparator.comparing(e -> e.getValue())).forEach(e -> asc3.put(e.getKey(), e.getValue()));
map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(f -> asc4.put(f.getKey(), f.getValue()));
//按照value降序排列:{d=4, b=3, c=2, a=1}
LinkedHashMap<String, String> desc3 = new LinkedHashMap<>();
LinkedHashMap<String, String> desc4 = new LinkedHashMap<>();
map.entrySet().stream().sorted(Collections.reverseOrder(Comparator.comparing(e -> e.getValue()))).forEach(f -> desc3.put(f.getKey(), f.getValue()));
map.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).forEach(f -> desc4.put(f.getKey(), f.getValue()));
2. List转Map
List<User> list = Arrays.asList(
new User(1, "A", "2022/03/26 10:23:21"),
new User(2, "B", "2022/03/26 10:35:40"),
new User(3, "C", "2022/03/26 09:26:38"));
//key和value都是对象的属性
Map<Long, String> map1 = list.stream().collect(Collectors.toMap(User::getId, User::getName));
//key是对象的属性,value是对象
Map<Long, User> map2 = list.stream().collect(Collectors.toMap(User::getId, User -> User));
//重复key处理:如果k1==k2,使用k2对应值覆盖k1
Collectors.toMap(User::getId, User -> User, (k1, k2) -> k2)
//根据user.sex将数组分组
Map<String, List<User>> map3 = list.stream().collect(Collectors.groupingBy(User::getSex));
等价写法
Collectors.toMap(User::getId, User -> User)
== Collectors.toMap(User::getId, Function.identity())
3. Map转List
Map<String, Integer> map = new HashMap<>();
map.put("a",2);
map.put("c",1);
map.put("b",3);
//默认顺序
List<User> list = map.entrySet().stream().map(e -> new User(e.getValue(), e.getKey())).collect(Collectors.toList());
//按value排序
List<User> list2 = map.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(e -> new User(e.getValue(), e.getKey())).collect(Collectors.toList());
4. 从List中获取某个属性
List<User> list = Arrays.asList(
new User(1, "A", "2022/03/26 10:23:21"),
new User(2, "B", "2022/03/26 10:35:40"),
new User(3, "C", "2022/03/26 09:26:38"),
new User(4, "C", "2022/03/26 11:16:38"));
//获取所有User.name
List<String> names = list.stream().map(e -> e.getName()).collect(Collectors.toList());
List<String> names2 = list.stream().map(User::getName).collect(Collectors.toList());
//拼接对象的几个属性
List<String> info = list.stream().map(e -> e.getId() + "-" + e.getName()).collect(Collectors.toList());
5. 筛选并根据属性去重
List<User> list = Arrays.asList(
new User(1, "A", "2022/03/26 10:23:21"),
new User(2, "B", "2022/03/26 10:35:40"),
new User(3, null, "2022/03/26 09:26:38"),
new User(4, "B", "2022/03/26 11:16:38"));
//去重
List<String> names = list.stream().map(e -> e.getName()).distinct().collect(Collectors.toList());
//过滤name不空的对象
List<User> users = list.stream().filter(e -> e.getName() != null).collect(Collectors.toList());
6. 计算:最大,最小,平均值,和
List<Integer> list = Arrays.asList(1,4,5,2,3);
int max = list.stream().mapToInt(Integer::intValue).max().getAsInt();
int max2 = list.stream().max(Integer::compareTo).get();
int min = list.stream().mapToInt(Integer::intValue).min().getAsInt();
long sum = list.stream().mapToInt(Integer::intValue).sum();
long count = list.stream().count();
double average = list.stream().mapToInt(Integer::intValue).average().getAsDouble();
//使用IntSummaryStatistics获取统计信息
IntSummaryStatistics statistics = list.stream().mapToInt(Integer::intValue).summaryStatistics();
max = statistics.getMax();
min = statistics.getMin();
sum = statistics.getSum();
count = statistics.getCount();
average = statistics.getAverage();
7. reduce
List<Integer> list = Arrays.asList(1,2,3,4,5);
int sum = list.stream().reduce((a, b) -> a + b).get();
int sum2 = list.stream().reduce(0, (a, b) -> a + b);
//返回结果的类型和流中数据类型不一致时
list = Arrays.asList(Integer.MAX_VALUE, Integer.MAX_VALUE);
long sum3 = list.stream().reduce(0L, (a, b) -> a + b, (a, b) -> 0L);