# filter
这是一个 Stream 的过滤转换,此方法会生成一个新的流,其中包含符合某个特定条件的所有元素。
// 过滤出大于 25 的数据 | |
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(5); | |
System.out.println("运行之前:" + before); | |
List<Integer> after = before.stream().filter(i -> i > 25).collect(Collectors.toList()); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 5] | |
运行之后:[30, 40] |
# Map
map 方法指对一个流中的值进行某种形式的转换。需要传递给它一个转换的函数作为参数。
// User(String name,int age) | |
// 将 user 中的 age 转为 list | |
List<User> before = new ArrayList<>(); | |
before.add(new User("zhangsan", 10)); | |
before.add(new User("lisi", 20)); | |
before.add(new User("zhangsan", 30)); | |
before.add(new User("wangwu", 40)); | |
before.add(new User("zhaoliu", 50)); | |
System.out.println("运行之前:" + before); | |
// 与下面写法相同 | |
// List<Integer> after = before.stream().map(user -> user.getAge()).collect(Collectors.toList()); | |
List<Integer> after = before.stream().map(User::getAge).collect(Collectors.toList()); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[zhangsan:10, lisi:20, zhangsan:30, wangwu:40, zhaoliu:50] | |
运行之后:[10, 20, 30, 40, 50] |
# fiatMap
上面用 map 方法进行流转换的时候,是对每个元素应用一个函数,并将返回的值收集到一个新的流中。但是如果有一个函数,它返回的不是一个值,而是一个包含多个值的流。但是你需要的是一个包含多个流中的元素的集合
List<Integer> oneBefore = new ArrayList<>(); | |
List<Integer> twoBefore = new ArrayList<>(); | |
oneBefore.add(1); | |
oneBefore.add(2); | |
oneBefore.add(3); | |
twoBefore.add(4); | |
twoBefore.add(5); | |
twoBefore.add(6); | |
System.out.println("运行之前:" + oneBefore); | |
System.out.println("运行之前:" + twoBefore); | |
Map<String,List<Integer>> map = new HashMap<>(); | |
map.put("1",oneBefore); | |
map.put("2",twoBefore); | |
// 返回的是一个流的集合,但是我需要的是 List<Integer > 这样一个集合 | |
List<Stream<Integer>> oneAfter = map.values().stream().map(Collection::stream).collect(Collectors.toList()); | |
// 这个时候就应该使用 flatMap 将多个流进行合并,然后再收集到一个集合中 | |
List<Integer> twoAfter = map.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); | |
System.out.println("运行之后:" + twoAfter); |
运行结果:
运行之前:[1, 2, 3] | |
运行之前:[4, 5, 6] | |
运行之后:[1, 2, 3, 4, 5, 6] |
# Limit
limit (n) 方法会返回一个包含 n 个元素的新的流(若总长小于 n 则返回原始流)。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(5); | |
System.out.println("运行之前:" + before); | |
List<Integer> after = before.stream().limit(3).collect(Collectors.toList()); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 5] | |
运行之后:[10, 20, 30] |
# Skip
skip (n) 方法正好相反,它会丢弃掉前面的 n 个元素。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(5); | |
System.out.println("运行之前:" + before); | |
List<Integer> after = before.stream().skip(3).collect(Collectors.toList()); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 5] | |
运行之后:[40, 5] |
用 limit 和 skip 方法一起使用就可以实现日常的分页功能
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(5); | |
// 当前页号 | |
int page = 1; | |
// 每页大小 | |
int pageSize = 2; | |
System.out.println("运行之前:" + before); | |
List<Integer> after = before.stream().skip(page * pageSize).limit(pageSize).collect(Collectors.toList()); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 5] | |
运行之后:[30, 40] |
# Distinct
根据原始流中的元素返回一个具有相同顺序、去除了重复元素的流,这个操作显然是需要记住之前读取的元素。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(30); | |
System.out.println("运行之前:" + before); | |
List<Integer> after = before.stream().distinct().collect(Collectors.toList()); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 30] | |
运行之后:[10, 20, 30, 40] |
# Sort
需要遍历整个流的,并在产生任何元素之前对它进行排序。因为有可能排序后集合的第一个元素会在未排序集合的最后一位。
List<Integer> before = new ArrayList<>(); | |
before.add(20); | |
before.add(10); | |
before.add(40); | |
before.add(50); | |
before.add(30); | |
System.out.println("------升序------"); | |
System.out.println("运行之前:" + before); | |
List<Integer> after = before.stream().sorted().collect(Collectors.toList()); | |
System.out.println("运行之后:" + after); | |
System.out.println("------降序------"); | |
System.out.println("运行之前:" + before); | |
Comparator<Integer> comparator = (o1, o2) -> o2 - o1; | |
List<Integer> after_2 = before.stream().sorted(comparator).collect(Collectors.toList()); | |
System.out.println("运行之后:" + after_2); |
运行结果:
------升序------ | |
运行之前:[20, 10, 40, 50, 30] | |
运行之后:[10, 20, 30, 40, 50] | |
------降序------ | |
运行之前:[20, 10, 40, 50, 30] | |
运行之后:[50, 40, 30, 20, 10] |
# Max 和 Min
返回流中最大值和最小值。
List<Integer> before = new ArrayList<>(); | |
before.add(20); | |
before.add(10); | |
before.add(40); | |
before.add(50); | |
before.add(30); | |
System.out.println("运行之前:" + before); | |
Integer max = before.stream().max(Integer::compareTo).get(); | |
Integer min = before.stream().min(Integer::compareTo).get(); | |
System.out.println("最大值:" + max); | |
System.out.println("最大值:" + min); |
运行结果:
运行之前:[20, 10, 40, 50, 30] | |
最大值:50 | |
最大值:10 |
# FindFirst
返回非空集合中的第一个值,它通常与 filter 方法结合起来使用。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(5); | |
System.out.println("运行之前:" + before); | |
Integer after = before.stream().filter(o -> o > 20).findFirst().get(); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 5] | |
运行之后:30 |
# FindAny
可以在集合中只要找到任何一个所匹配的元素,就返回,此方法在对流并行执行时十分有效(任何片段中发现第一个匹配元素都会结束计算,串行流中和 findFirst 返回一样)。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(5); | |
System.out.println("运行之前:" + before); | |
Integer after = before.stream().filter(o -> o > 20).findAny().get(); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 5] | |
运行之后:30 |
# AnyMatch
可以判定集合中是否还有匹配的元素。返回结果是一个 boolean 类型值。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(5); | |
System.out.println("运行之前:" + before); | |
boolean after_1 = before.stream().anyMatch(o -> o > 20); | |
boolean after_2 = before.stream().anyMatch(o -> o > 40); | |
System.out.println("运行之后:" + after_1); | |
System.out.println("运行之后:" + after_2); |
运行结果:
运行之前:[10, 20, 30, 40, 5] | |
运行之后:true | |
运行之后:false |
# AllMatch 和 NonoMatch
分别在所有元素匹配和没有元素匹配时返回 true。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(5); | |
System.out.println("运行之前:" + before); | |
boolean after_1 = before.stream().allMatch(o -> o > 20); | |
boolean after_2 = before.stream().noneMatch(o -> o > 40); | |
System.out.println("运行之后:" + after_1); | |
System.out.println("运行之后:" + after_2); |
运行结果:
运行之前:[10, 20, 30, 40, 5] | |
运行之后:false | |
运行之后:true |
# Reduce
将流中的元素进行进一步计算的方法。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(50); | |
// 求和 | |
System.out.println("运行之前:" + before); | |
Integer after_1 = before.stream().reduce((x, y) -> x + y).get(); | |
System.out.println("运行之后:" + after_1); | |
// 优化 | |
Integer after_2 = before.stream().reduce(Integer::sum).get(); | |
System.out.println("运行之后:" + after_2); | |
// 含有初始标识的,求和 | |
Integer after_3 = before.stream().reduce(10, Integer::sum); | |
System.out.println("运行之后:" + after_3); | |
// 对元素的长度进行求和 ((total,y)->total+y.toString ().length (),类似于一个累加器,会被重复调用) | |
Integer after_4 = before.stream().reduce(0, (o, x) -> o + x.toString().length(), (x, y) -> x + y); | |
System.out.println("运行之后:" + after_4); | |
// 优化 | |
Integer after_5 = before.stream().map(Objects::toString).mapToInt(String::length).sum(); | |
System.out.println("运行之后:" + after_5); |
运行结果:
运行之前:[10, 20, 30, 40, 50] | |
运行之后:150 | |
运行之后:150 | |
运行之后:160 | |
运行之后:10 | |
运行之后:10 |
# 收集结果(Collectors)
将一个流收集到一个 List 中。
List<Integer> thereList = before.stream().collect(Collectors.toList()); |
收集到 Set 中。
Set<Integer> thereSet = before.stream().collect(Collectors.toSet()); |
收集到 Set 时,控制 Set 的类型。
TreeSet<Integer> treeSet = before.stream().collect(Collectors.toCollection(TreeSet::new)); |
# 拼接(joining)
将字流中的字符串连接并收集起来。
List<String> before = new ArrayList<>(); | |
before.add("10"); | |
before.add("20"); | |
before.add("30"); | |
before.add("40"); | |
before.add("50"); | |
System.out.println("运行之前:" + before); | |
String after = before.stream().collect(Collectors.joining()); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 50] | |
运行之后:1020304050 |
在将流中的字符串连接并收集起来时,想在元素中介添加分隔符,传递个 joining 方法即可。
String after = before.stream().collect(Collectors.joining(",")); |
运行结果:
运行之前:[10, 20, 30, 40, 50] | |
运行之后:10,20,30,40,50 |
当流中的元素不是字符串时,需要先将流转成字符串流再进行拼接。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(50); | |
System.out.println("运行之前:" + before); | |
String after = before.stream().map(Objects::toString).collect(Collectors.joining()); | |
// 同理 | |
// String after_2 = before.stream().map(String::valueOf).collect(Collectors.joining(",")); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[10, 20, 30, 40, 50] | |
运行之后:1020304050 |
# 收集聚合(summingInt、averagingInt、maxBy、minBy)
分别收集流的总和、平均值、最大值或者最小值。
List<Integer> before = new ArrayList<>(); | |
before.add(10); | |
before.add(20); | |
before.add(30); | |
before.add(40); | |
before.add(50); | |
System.out.println("运行之前:" + before); | |
int sum = before.stream().collect(Collectors.summingInt(Integer::intValue)); | |
// 另一种写法 | |
// int sum = before.stream().mapToInt(Integer::intValue).sum(); | |
System.out.println("求和:" + sum); | |
Double ave = before.stream().collect(Collectors.averagingInt(Integer::intValue)); | |
System.out.println("平均值:" + ave); | |
Integer max = before.stream().collect(Collectors.maxBy(Integer::compare)).get(); | |
// 另一种写法 | |
// Integer max = before.stream().max(Integer::compare).get(); | |
System.out.println("最大值:" + max); | |
Integer min = before.stream().collect(Collectors.minBy(Integer::compare)).get(); | |
// 另一种写法 | |
// Integer min = before.stream().min(Integer::compare).get(); | |
System.out.println("最小值:" + min); |
运行结果:
运行之前:[10, 20, 30, 40, 50] | |
求和:150 | |
平均值:30.0 | |
最大值:50 | |
最小值:10 |
# 将结果集收集到 Map(toMap)
将集合中的元素收集到 Map 中时,可以使用 Collectors.toMap 方法。这个方法有两个参数,用来生成 Map 的 key 和 value。
List<Student> students = new ArrayList<>(); | |
students.add(new Student("1","11", "zhangsan")); | |
students.add(new Student("2","22", "lisi")); | |
students.add(new Student("3","33", "wangwu")); | |
students.add(new Student("4","44", "zhaoliu")); | |
System.out.println("运行之前:" + students); | |
Map<String, String> after = students.stream().collect(Collectors.toMap(Student::getId, Student::getName)); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[{id = 1,code = 11,name = zhangsan}, {id = 2,code = 22,name = lisi}, {id = 3,code = 33,name = wangwu}, {id = 4,code = 44,name = zhaoliu}] | |
运行之后:{1=zhangsan, 2=lisi, 3=wangwu, 4=zhaoliu} |
如果多个元素拥有相同的键,在收集结果时会抛出 java.lang.IllegalStateException 异常。可以使用第三个参数来解决,第三个参数用来确定当出现键冲突时,该如何处理结果,如果当出现键冲突时只保留一个并且是保留已经存在的值时,就是如下方式。
Map<String, String> after = students.stream().collect(Collectors.toMap(Student::getId, Student::getName, (l, r) -> l)); |
如果想指定生成的 Map 类型,则还需要第三个参数。
TreeMap<String, String> afte = students.stream().collect(Collectors.toMap(Student::getId, Student::getName, (l, r) -> l, TreeMap::new)); |
# 分组(groupingBy)
List<demo.toMap.Student> students = new ArrayList<>(); | |
students.add(new demo.toMap.Student("1","11", "zhangsan")); | |
students.add(new demo.toMap.Student("2","22", "lisi")); | |
students.add(new demo.toMap.Student("3","22", "wangwu")); | |
students.add(new demo.toMap.Student("4","44", "zhaoliu")); | |
System.out.println("运行之前:" + students); | |
Map<String, List<Student>> after = students.stream().collect(Collectors.groupingBy(Student::getCode)); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[{id = 1,code = 11,name = zhangsan}, {id = 2,code = 22,name = lisi}, {id = 3,code = 22,name = wangwu}, {id = 4,code = 44,name = zhaoliu}] | |
运行之后:{44=[{id = 4,code = 44,name = zhaoliu}], 22=[{id = 2,code = 22,name = lisi}, {id = 3,code = 22,name = wangwu}], 11=[{id = 1,code = 11,name = zhangsan}]} |
# 分片(partitioningBy)
当分类函数是一个返回布尔值的函数时,流元素会被分为两组列表:一组是返回 true 的元素集合,另一组是返回 false 的元素集合。这种情况适用 partitoningBy 方法会比 groupingBy 更有效率。
List<demo.toMap.Student> students = new ArrayList<>(); | |
students.add(new demo.toMap.Student("1","11", "zhangsan")); | |
students.add(new demo.toMap.Student("2","22", "lisi")); | |
students.add(new demo.toMap.Student("3","22", "wangwu")); | |
students.add(new demo.toMap.Student("4","44", "zhaoliu")); | |
System.out.println("运行之前:" + students); | |
Map<Boolean, List<demo.toMap.Student>> after = students.stream().collect(Collectors.partitioningBy(o -> "22".equals(o.getCode()))); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之前:[{id = 1,code = 11,name = zhangsan}, {id = 2,code = 22,name = lisi}, {id = 3,code = 22,name = wangwu}, {id = 4,code = 44,name = zhaoliu}] | |
运行之后:{false=[{id = 1,code = 11,name = zhangsan}, {id = 4,code = 44,name = zhaoliu}], true=[{id = 2,code = 22,name = lisi}, {id = 3,code = 22,name = wangwu}]} |
# 扩展功能
无论是 groupingBy 方法还是 partitioningBy 方法都是支持的。
List<Student> students = new ArrayList<>(); | |
students.add(new Student("1","11", "zhangsan")); | |
students.add(new Student("2","22", "lisi")); | |
students.add(new Student("3","22", "wangwu")); | |
students.add(new Student("4","44", "zhaoliu")); |
counting 方法会返回收集元素的总个数。
// 分组之后,每组的个数 | |
Map<String, Long> after = students.stream().collect(Collectors.groupingBy(Student::getCode, Collectors.counting())); | |
System.out.println("运行之后:" + after); |
运行结果:
运行之后:{44=1, 22=2, 11=1} |
summing (Int|Long|Double) 方法接受一个取值函数作为参数,来计算总和。
// 分组之后,各组名字的长度总和 | |
Map<String, Integer> after = students.stream().collect(Collectors.groupingBy(Student::getCode, Collectors.summingInt(o-> o.getName().length()))); | |
System.out.println("运行之后:" + after_2); |
运行结果:
运行之后:{44=7, 22=10, 11=8} |
maxBy 方法和 minBy 方法接受比较器作为参数来计算最大值和最小值。
Map<String, Optional<Student>> after_1 = students.stream().collect(Collectors.groupingBy(Student::getCode, Collectors.maxBy(Comparator.comparing(Student::getCode)))); | |
Map<String, Optional<Student>> after_2 = students.stream().collect(Collectors.groupingBy(Student::getCode, Collectors.minBy(Comparator.comparing(Student::getCode)))); | |
System.out.println("运行之后:" + after_1); | |
System.out.println("运行之后:" + after_2); |
运行结果:
运行之后:{44=Optional[{id = 4,code = 44,name = zhaoliu}], 22=Optional[{id = 2,code = 22,name = lisi}], 11=Optional[{id = 1,code = 11,name = zhangsan}]} | |
运行之后:{44=Optional[{id = 4,code = 44,name = zhaoliu}], 22=Optional[{id = 2,code = 22,name = lisi}], 11=Optional[{id = 1,code = 11,name = zhangsan}]} |
mapping 方法会将结果应用到另一个收集器上。
Map<String, Optional<String>> after = students.stream().collect(Collectors.groupingBy(Student::getCode, Collectors.mapping(Student::getId, Collectors.maxBy(Comparator.comparing(Integer::valueOf))))); | |
System.out.println("运行之后:" + after); | |
System.out.println("运行之后:" + JSON.toJSONString(after)); |
运行结果:
运行之后:{44=Optional[4], 22=Optional[3], 11=Optional[1]} | |
运行之后:{44=4, 22=3, 11=1} |
# 案例
# List<List<xxx>> 转化为 List<xxx>
将嵌套的 list 合并成一个 list
List<String> list1 = new ArrayList<>(); | |
list1.add("111"); | |
list1.add("222"); | |
list1.add("333"); | |
List<String> list2 = new ArrayList<>(); | |
list2.add("444"); | |
list2.add("555"); | |
list2.add("666"); | |
List<List<String>> before = List.of(list1, list2); | |
System.out.println("合并之前:" + Arrays.toString(before.toArray())); | |
// 合并 | |
List<String> after = before.stream().collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll); | |
System.out.println("合并之后:" + Arrays.toString(after.toArray())); |
运行结果
合并之前:[[111, 222, 333], [444, 555, 666]] | |
合并之后:[111, 222, 333, 444, 555, 666] |
待补充...