Java 8 的 Stream API 是一个比较重要也比较常用的新特性,正确的使用 Stream API 可以简化集合操作,让我们的代码更加干净、整洁和高效。但是 Stream API 的部分方法用法还是比较复杂的,有时候虽然知道有这样的方法可以用,但是就是想不起来具体写法,还要再去查API看文档,因此整理了一下项目开发中用过的 Stream API 代码片段,作为代码模板。主要作为自己的一份笔记,也可以让还不熟悉 Stream API 的同学来“套用”。
本文只整理代码模板,不讨论任何 Stream API 的理论和用法(想学的同学可以自己搜索一些资料哈),当然 Stream API 的使用也是多种多样的,不可能都列举出来,这里只整理了本人在项目中用到的,后面发现有新的再继续加进来。
1、流元素匹配和聚合操作汇总
List<Integer> list = new ArrayList<>(0);
boolean isAllMatch = list.stream().allMatch(i -> i > 5); // 是否全部元素都满足条件
boolean isNoneMatch = list.stream().noneMatch(i -> i > 5); // 是否没有元素满足条件
boolean isAnyMatch = list.stream().anyMatch(i -> i > 5); // 是否有任意元素满足条件
Optional<Integer> first = list.stream().findFirst(); // 获取第一个元素
Optional<Integer> any = list.stream().findAny(); // 获取任意一个元素
long count = list.stream().count(); // 统计元素总数
Optional<Integer> max = list.stream().max(Integer::compareTo); // 获取元素最大值
Optional<Integer> min = list.stream().min(Integer::compareTo); // 获取元素最小值
2、流元素筛选、分割操作汇总
List<String> list = new ArrayList<>(0);
list = list.stream().filter(s -> s.startsWith("abc")).collect(Collectors.toList()); // 过滤出符合条件的元素
list = list.stream().skip(10L).collect(Collectors.toList()); // 跳过(前)指定个数的元素
list = list.stream().limit(10L).collect(Collectors.toList()); // 截取(前)指定个数的元素
list = list.stream().distinct().collect(Collectors.toList()); // 过滤掉重复元素
3、String集合滤空、去重、排序
List<String> list = new ArrayList<>(0);
list = list.stream().filter(StringUtils::isNotBlank).distinct().sorted().collect(Collectors.toList());
4、对象List按某一属性倒序排序
List<User> list = new ArrayList<>(0);
list = list.stream().sorted(Comparator.comparing(User::getCreateTime, Comparator.reverseOrder())).collect(Collectors.toList());
// 或
list = list.stream().sorted(Comparator.comparing(User::getCreateTime).reversed()).collect(Collectors.toList());
5、对象List先按某一属性排序,再按另一属性排序,并指定null值排序方式
List<User> list = new ArrayList<>(0);
list = list.stream()
.sorted(Comparator.comparing(User::getCreateTime, Comparator.nullsLast(Date::compareTo)))
.sorted(Comparator.comparing(User::getId, Comparator.nullsLast(String::compareTo)))
.collect(Collectors.toList());
// 或
list = list.stream()
.sorted(Comparator.comparing(User::getCreateTime,Comparator.nullsLast(Date::compareTo))
.thenComparing(User::getId, Comparator.nullsLast(String::compareTo)))
.collect(Collectors.toList());
6、对象List获取某一属性List
List<User> list = new ArrayList<>(0);
List<String> list1 = list.stream().map(User::getId).collect(Collectors.toList());
7、对象List转成另一对象List
public void f1() {
// ...
List<User> list = new ArrayList<>(0);
List<Person> list1 = list.stream().map(this::toPerson).collect(Collectors.toList());
// ...
}
private Person toPerson(User user) {
// User 对象转 Person 对象
return new Person();
}
8、对象List转成以属性为key,对象本身为值的Map
Map<String, User> map = list.stream().collect(Collectors.toMap(User::getId, Function.identity(), (first, next) -> first));
9、对象List转成以属性为key,以另一属性为值的Map
List<User> list = new ArrayList<>(0);
Map<String, String> map = list.stream().collect(Collectors.toMap(User::getId, User::getDeptId, (first, next) -> first));
10、对象List以某一属性分组
List<User> list = new ArrayList<>(0);
Map<String, List<User>> map = list.stream().collect(Collectors.groupingBy(User::getDeptId));
11、对象List以某一属性分组计数
List<User> list = new ArrayList<>(0);
Map<String, Long> map = list.stream().collect(Collectors.groupingBy(User::getDeptId, Collectors.counting()));
12、对象List以某一属性对另一属性分组
List<User> list = new ArrayList<>(0);
Map<String, List<String>> map = list.stream()
.collect(Collectors.groupingBy(User::getDeptId, Collectors.mapping(User::getId, Collectors.toList())));
13、对象嵌套集合拆解成对象集合(多个集合合并)
List<List<User>> list = new ArrayList<>(0);
List<User> list1 = list.stream().flatMap(Collection::stream).collect(Collectors.toList());
// 或
List<User> list1 = list.stream().collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
// 或
List<User> list1 = list.stream().reduce(ListUtils::union).orElse(new ArrayList<>(0));
// 或
List<User> list1 = list.stream().reduce(new ArrayList<>(), (all, item ) -> {all.addAll(item); return all;});
14、int、double 和 long 类型求和之外的运算
List<Integer> list = new ArrayList<>(0);
int product = list.stream().reduce(1, (a, b) -> a * b);
List<Order> list = new ArrayList<>(0);
BigDecimal totalPrice = list.stream().map(Order::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
15、对象Lis按对象某一属性去重
List<User> list = new ArrayList<>(0);
list = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getId))), ArrayList::new));
// 或
public void f1() {
// ...
List<User> list = new ArrayList<>(0);
list = list.stream().filter(distinctById(User::getId)).collect(Collectors.toList());
// ...
}
private static Predicate<User> distinctById(Function<User, String> function) {
Map<String, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(function.apply(t), Boolean.TRUE) == null;
}
注意:流操作的灵活性和强大之处在于将各种中间操作按需组合再配合各种收集器,实现多种操作的链式调用,这种方式虽然能减少代码量,简化操作,但是不可避免的降低了代码的可读性,如果在变量命名等方面再“任性”一下,最后的效果大家可以自行脑补混淆之后的 JS 代码。因此一行代码中连接的操作不宜过多,操作中的条件判断等表达式不应该过于复杂,变量等命名也应该尽量有意义,这个程度就只能由使用者自己把握了