1.遍历/匹配(foreach/find/match)
Stream
也是支持类似集合的遍历和匹配元素的,只是Stream
中的元素是以Optional
类型存在的。Stream
的遍历、匹配非常简单。
List list = Arrays.asList(1, 2, 3, 4, 5, 7, 8, 9, 0, 100);// find使用,查找第一个元素Optional first = list.stream().findFirst();log.info(first.get().toString());// match使用,判断是否存在某个值boolean b1 = list.stream().anyMatch(value -> value >= 100);boolean b2 = list.stream().anyMatch(value -> value > 10);log.info(String.valueOf(b1));log.info(String.valueOf(b2));// foreach使用,遍历输出元素list.stream().filter(value -> value > 4).forEach(System.out::print);System.out.println();list.forEach(System.out::print);
2.筛选(filter)
筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。
// 数字筛选List list = Arrays.asList(1, 3, 4, 5, 6, 7, 8, 9, 10, 20);ArrayList arrayList1 = new ArrayList();list.stream().filter(value -> value > 4).forEach(value -> arrayList1.add(value));log.info(arrayList1.toString());ArrayList arrayList2 = new ArrayList();list.stream().filter(value -> value <= 4).forEach(arrayList2::add);log.info(arrayList2.toString());// 对象筛选List userList1 = Arrays.asList( new User(1, "xw", "男"), new User(2, "zgx", "男"), new User(3, "gg", "男"), new User(4, "whb", "男"), new User(5, "yda", "男"), new User(6, "bhm", "女"));List userList2 = new ArrayList();userList1.stream().filter(user -> user.getId() > 2).forEach(userList2::add);log.info(userList2.toString());userList1.stream().filter(user -> user.getName().equals("xw")).forEach(System.out::println);HashMap<String, Optional> userHashMap = new HashMap();Optional man = userList1.stream().filter(user -> user.getSex().equals("男")).findFirst(); userHashMap.put("man", man); log.info(userHashMap.toString());
3.聚合(max/min/count)
max
、min
、count
这些字眼你一定不陌生,没错,在mysql中我们常用它们进行数据统计。Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。
max、min、count
// max & min & countList list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 89, 9, 0, 10, 20, 30);Optional max = list.stream() .max(Comparator.comparing(Integer::intValue));log.info(String.format("最大值是:%d", max.get()));Optional min = list.stream() .min(Comparator.comparing(value -> value.intValue()));log.info(String.format("最小值是:%d", min.get()));Integer count1 = Math.toIntExact(list.stream().count());log.info(String.format("list总元素量1为:%d", count1));Integer count2 = Math.toIntExact(list.stream().filter(value -> value > 5).count());log.info(String.format("list元素值大于5的个数:%d", count2));List userList = Arrays.asList( new User(1, "xw", "男", 22), new User(2, "zgx", "男", 22), new User(3, "whb", "男", 23), new User(4, "gg", "男", 30), new User(5, "yda", "男", 22), new User(6, "bhm", "女", 22), new User(7, "lwn", "女", 22));Optional ageMax = userList.stream().max(Comparator.comparing(value -> value.getAge()));log.info(String.format("年龄最大的是:%s", ageMax.get()));Optional ageMin = userList.stream().filter(user -> user.getSex().equals("男")).min(Comparator.comparing(User::getAge));log.info(String.format("性别为男且年龄最小的:%s", ageMin.get()));Integer count3 = Math.toIntExact(userList.stream().filter(user -> user.getAge() > 22).count());log.info(String.format("年龄大于22的用户数量为:%d", count3));
4.映射(map/flatMap)
映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map
和flatMap
:
map
:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。flatMap
:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
map
// mapList list1 = Arrays.asList(1, 3, 5, 6, 7, 8, 0, 10, 20, 22, 39);List collect1 = list1.stream().filter(value -> value > 7).collect(Collectors.toList());log.info(String.format("list1元素值大于7的有: %s", collect1));List list2 = Arrays.asList("xw", "sjdk", "sf", "jk", "hoksh", "shdfj", "jhgkj");List collect2 = list2.stream().map(String::toUpperCase).collect(Collectors.toList());log.info(String.format("list2元素值全转大写,结果:%s", collect2));List userList1 = Arrays.asList( new User(1, "xw", "男", 22), new User(2, "zgx", "男", 22), new User(3, "whb", "男", 23), new User(4, "gg", "男", 30), new User(5, "yda", "男", 22), new User(6, "bhm", "女", 22), new User(7, "lwn", "女", 22), new User(8, "ksj", "女", 22));List userList2 = userList1.stream() .map(user -> { if (user.getSex().equals("女")) { user.setAge(user.getAge() - 2); } user.setName(user.getName().toUpperCase()); return user; }) .filter(user -> user.getAge() > 19 && user.getSex().equals("女")) .collect(Collectors.toList());log.info(String.format("修改结果为:%s", userList2));
flatMap
// flatMapList stringList = userList1.stream() .flatMap(user -> { Stream stream = Arrays.stream(user.toString().split("=")); return stream; }) .collect(Collectors.toList());log.info(String.format("flatMap处理前:%s", userList1));log.info(String.format("flatMap转换结果:%s", stringList));
5.归约(reduce)
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和
、求乘积
和求最值
操作。
// 求和List list1 = Arrays.asList(1, 3, 5, 2, 1, 5, 89, 23, 89, 23, 34);Integer sum = list1.stream().reduce(0, Integer::sum);log.info(String.format("list1中各元素之和:%d", sum));// 求积List list2 = Arrays.asList(1, 2, 4);Optional product = list2.stream().reduce((x, y) -> x * y);log.info(String.format("list中2各元素之积:%d", product.get()));// 求最大值1Optional max1 = list1.stream().reduce(Integer::max);log.info(String.format("list1中的最大值是:%d", max1.get()));// 求最大/小值2Optional min1 = list1.stream().reduce((x, y) -> x < y ? x : y);log.info(String.format("list1中的最小值:%d", min1.get()));
List userList1 = Arrays.asList( new User(1, "xw", "男", 22), new User(2, "zgx", "男", 22), new User(3, "whb", "男", 23), new User(4, "gg", "男", 30), new User(5, "yda", "男", 22), new User(6, "bhm", "女", 23), new User(7, "lsn", "女", 22), new User(8, "ksj", "女", 22));Integer maxAge1 = userList1.stream().reduce(0, (maxAge, user) -> maxAge > user.getAge() ? maxAge : user.getAge(), Integer::max);log.info(String.format("年龄最大是:%d", maxAge1));Optional max2 = userList1.stream().map(User::getAge).reduce(Integer::max);Optional max3 = userList1.stream().map(User::getAge).reduce((x, y) -> x > y ? x : y);log.info(String.format("年龄最大是:%d", max2.get()));log.info(String.format("年龄最大是:%d", max3.get()));Integer stringMaxLength = userList1.stream() .filter(user -> user.getAge() > 22 && user.getAge() { Stream newStream = Arrays.stream(user.toString().split("=")); return newStream; }) .collect(Collectors.toList()) .stream().map(String::toUpperCase) .reduce(0, (maxLength, string) -> maxLength > string.length() ? maxLength : string.length(), Integer::max);log.info(String.format("最大字符串长度为:%s", stringMaxLength));
6.收集(collect)
collect
,收集,可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。
collect主要依赖java.util.stream.Collectors类内置的静态方法。
6.1归集(toList/toSet/toMap)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList
、toSet
和toMap
比较常用,另外还有toCollection
、toConcurrentMap
等复杂一些的用法。
toList
List list1 = Arrays.asList(1, 2, 8, 0, 9, 0, 1, 23, 32, 37, 49, 48);List collect1 = list1.stream().filter(value -> value > 5).collect(Collectors.toList());log.info(String.format("list1中收集>5的结果为:%s", collect1));
toSet
Set collect2 = list1.stream().filter(value -> value < 5).collect(Collectors.toSet());log.info(String.format("list1中收集 {System.out.print(value + " ");
toMap
List userList1 = Arrays.asList( new User(1, "xww", "男", 22), new User(2, "zgx", "男", 22), new User(3, "whb", "男", 23), new User(4, "gg", "男", 30), new User(5, "yda", "男", 22), new User(6, "bhm", "女", 23), new User(7, "lsn", "女", 22), new User(8, "ksj", "女", 22));Map userMap = userList1.stream() .filter(user -> user.getSex().equals("女")) .collect(Collectors.toMap(User::getName, user -> user));log.info(String.format("性别为女的用户转map:%s", userMap));
6.2 统计(count/averaging)
Collectors
提供了一系列用于数据统计的静态方法:
- 计数:
count
- 平均值:
averagingInt
、averagingLong
、averagingDouble
- 最值:
maxBy
、minBy
- 求和:
summingInt
、summingLong
、summingDouble
- 统计以上所有:
summarizingInt
、summarizingLong
、summarizingDouble
List list = Arrays.asList(1, 2, 3, 2, 5, 3, 9, 8, 7, 6, 29, 10, 22);// countlong count = list.stream().filter(value -> value > 10).count();log.info(String.format("list中元素>10的个数为:%d", count));// averageDouble average = list.stream().filter(value -> value > 1).collect(Collectors.averagingInt(Integer::intValue));log.info(String.format("list中元素>1的元素平均值为:%.2f", average));List userList1 = Arrays.asList( new User(1, "xww", "女", 22), new User(2, "zgx", "男", 22), new User(3, "whb", "男", 23), new User(4, "gg", "男", 30), new User(5, "yda", "男", 22), new User(6, "bhm", "女", 23), new User(7, "lsn", "女", 22), new User(8, "ksj", "女", 22));Double averageAge = userList1.stream() .filter(user -> user.getSex().equals("男")) .map(User::getAge) .collect(Collectors.averagingInt(Integer::intValue));log.info(String.format("男用户的平均年龄为:%d 岁", averageAge.intValue()));// mapToIntint ageSum = userList1.stream() .filter(user -> user.getSex().equals("女")) .mapToInt(User::getAge) .sum();log.info(String.format("女用户的年龄之和为:%d", ageSum));// summarizingInt 统计 计数、总和、最小值、平均值、最大值IntSummaryStatistics recording = userList1.stream() .filter(user -> user.getSex().equals("男")) .collect(Collectors.summarizingInt(User::getAge));log.info(String.format("记录所有男用户的年龄各项值,结果为:%s", recording));
6.3 分组(partitioningBy/groupingBy)
- 分区:将
stream
按条件分为两个Map
,比如员工按薪资是否高于8000分为两部分。 - 分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
partitioningBy
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 20, 37, 49, 243, 30);// partitioningByMap<Boolean, List> collect1 = list.stream() .collect(Collectors.partitioningBy(value -> value > 20));log.info(String.format("元素值是否大于20进行分组,结果为:%s", collect1));collect1.forEach((key, value) -> { log.info(String.format("元素值是否大于20进行分组,结果为:%s:%s", key, value));});
groupingBy
List userList = Arrays.asList( new User(1, "xww", "女", 22), new User(2, "zgx", "男", 21), new User(3, "whb", "男", 23), new User(4, "gg", "男", 30), new User(5, "yda", "男", 22), new User(6, "bhm", "女", 23), new User(7, "lsn", "女", 22), new User(8, "ksj", "女", 22));// groupingByMap<String, List> collect2 = userList.stream() .collect(Collectors.groupingBy(User::getSex));log.info(String.format("根据性别对用户进行分组,结果为:%s", collect2));collect2.forEach((key, user) -> { log.info(String.format("根据性别对用户进行分组,结果为:%s:%s", key, user));});
6.4 接合(joining)
joining
可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 20, 37, 49, 243, 30);String collect = list.stream() .map(Object::toString) .collect(Collectors.joining("——"));log.info(String.format("joining测试结果为:%s", collect));
7.排序(sorted)
sorted,中间操作。有两种排序:
- sorted():自然排序,流中元素需实现Comparable接口
- sorted(Comparator com):Comparator排序器自定义排序
List userList1 = Arrays.asList( new User(1, "xw", "女", 22), new User(2, "zgx", "男", 21), new User(3, "whb", "男", 23), new User(4, "gg", "男", 30), new User(5, "yda", "男", 22), new User(6, "bhm", "女", 23), new User(7, "lsn", "女", 22), new User(8, "ksj", "女", 22));// sortedList userList2 = userList1.stream() .sorted(Comparator.comparing(User::getAge)) .collect(Collectors.toList());log.info(String.format("按照年龄排序,结果为:%s", userList2));// 从小到大,正序List userName1 = userList1.stream() .sorted(Comparator.comparing(User::getAge)) .map(User::getName) .collect(Collectors.toList());log.info(String.format("根据年龄从小到大排序:%s", userName1));// 从大到小,倒序List userName2 = userList1.stream() .filter(user -> user.getSex().equals("男")) .sorted(Comparator.comparing(User::getAge).reversed()) .map(User::getName) .collect(Collectors.toList());log.info(String.format("男用户根据年龄从大到小排序:%s", userName2));
8.提取/组合
流也可以进行合并
、去重
、限制
、跳过
等操作。
1.去重排序
List list = Arrays.asList(1, 2, 4, 4, 10, 9, 6, 8, 6, 2, 3, 7, 5);List collect = list .stream() .distinct() .sorted(Comparator.comparing(Integer::intValue)) .collect(Collectors.toList());collect.forEach(x -> System.out.print(x+" ")); // 1 2 3 4 5 6 7 8 9 10
存在重复数据的问题,这里使用stream流的衍生功能,去除一个对象中的部分元素的重复如下:
List userList = Arrays.asList( new User(1, "xw", "女", 21), new User(2, "zgx", "男", 21), new User(3, "whb", "男", 23), new User(4, "gag", "男", 30), new User(4, "gbg", "男", 30), new User(4, "gcg", "女", 30), new User(5, "yda", "男", 22), new User(6, "bhm", "女", 23), new User(7, "lsn", "女", 22), new User(8, "ksj", "女", 22));ArrayList collect1 = userList.stream().collect(Collectors.collectingAndThen( Collectors.toCollection(() -> new TreeSet( Comparator.comparing( User::getId))), ArrayList::new));
多个字段或者多个条件去重
ArrayList collect2 = userList.stream().collect(Collectors.collectingAndThen( Collectors.toCollection(() -> new TreeSet( Comparator.comparing(user->user.getName() + ";" + user.getId()))), ArrayList::new)
以上使用到了collectingAndThen()根据属性进行去重的操作,进行结果集的收集,收集到结果集之后再进行下一步的处理。在这个去重操作中还用到了toCollection、TreeSet两个操作。
public static Collector collectingAndThen(Collector downstream,Function finisher)
看源码中需要传的参数有两个,第一个参数是Collector的子类,所以Collectors类中的大多数方法都能使用,比如:toList(),toSet(),toMap()等,当然也包括collectingAndThen()。第二个参数是一个Function函数,也是去重的关键,用到的ArrayList::new调用到了ArrayList的有参构造。Function函数是R apply(T t),在第一个参数downstream放在第二个参数Function函数的参数里面,将结果设置为t。对于toCollection是一个通用的方法,满足treeSet收集集合,再传入需要根据某个属性进行比较的比较器,就能达到去重的效果。
2.限制长度(limit)
List list = Arrays.asList(1, 2, 7, 3, 2, 2, 3, 4, 5, 2, 5, 6, 7, 8, 9, 0, 12);List collect1 = list .stream() .distinct() .sorted(Comparator.comparing(Integer::intValue)) .limit(6) .collect(Collectors.toList());collect1.forEach(x -> System.out.print(x + " "));
3.跳过(skip)
// 跳过前几项List list = Arrays.asList(1, 2, 7, 3, 2, 2, 3, 4, 5, 2, 5, 6, 7, 8, 9, 0, 12);List collect2 = list .stream() .distinct() .sorted(Comparator.comparing(Integer::intValue)) .skip(3) .limit(6) .collect(Collectors.toList());collect2.forEach(x -> System.out.print(x + " "));