收集Stream流中的结果
1. 将流中数据收集到集合中
Stream流提供 collect 方法,其参数需要一个 java.util.stream.Collector<T,A, R> 接口对象来指定收集到哪
种集合中。java.util.stream.Collectors 类提供一些方法,可以作为 Collector接口的实例:
public static <T> Collector<T, ?, List<T>> toList() :转换为 List 集合。
public static <T> Collector<T, ?, Set<T>> toSet() :转换为 Set 集合。
基本使用:
@Test
public void testStreamToCollection() {
Stream<String> stream = Stream.of("aa", "bb", "cc", "bb");
// 将流中数据收集到集合中
List<String> list = stream.collect(Collectors.toList());
System.out.println("list = " + list);
Set<String> set = stream.collect(Collectors.toSet());
System.out.println("set = " + set);
// 收集到指定的集合中
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
System.out.println("arrayList = " + arrayList);
HashSet<String> hashSet = stream.collect(Collectors.toCollection(HashSet::new));
System.out.println("hashSet = " + hashSet);
}
2. 将流中的数据收集到数组中
Stream提供 toArray 方法来将结果放到一个数组中,返回值类型是Object[]的:
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
// 其中,IntFunction接口有一个抽象方法R
R apply(int value);
基本使用:
@Test
public void testStreamToArray() {
Stream<String> stream = Stream.of("aa", "bb", "cc");
// 转成Object数组不方便
// Object[] objects = stream.toArray();
// for (Object o : objects) {
// System.out.println("o = " + o);
// }
// String[]
String[] strings = stream.toArray(String[]::new);
for (String string : strings) {
System.out.println("string = " + string + ", 长度: " + string.length());
}
}
// 输出结果:
string = aa, 长度: 2
string = bb, 长度: 2
string = cc, 长度: 2
3. 对流中数据进行聚合计算
当我们使用Stream流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作。比如获取最大值,获取最小值,求总和,平均值,统计数量。
java.util.stream.Collectors 类提供一些方法,可以实现上述操作。
3.1 maxBy、minBy
通过Collectors中的maxBy和minBy方法,可以获取流中的数据的最大值和最小值,并将最后的结果返回。
public static <T> Collector<T, ?, Optional<T>>
maxBy(Comparator<? super T> comparator) {
return reducing(BinaryOperator.maxBy(comparator));
}
// 其中Comparator接口有一个抽象方法
int compare(T o1, T o2);
基本使用:
@Test
public void testStreamToOther() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 58, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
// 获取最大值
Optional<Student> max = studentStream.collect(Collectors.maxBy((s1, s2) -> s1.getSocre() - s2.getSocre()));
System.out.println("最大值: " + max.get());
Optional<Student> min = studentStream.collect(Collectors.minBy((s1, s2) -> s1.getSocre() - s2.getSocre()));
System.out.println("最小值: " + min.get());
}
// 输出结果:
最大值:99
最小值:77
3.2 summingInt
通过Collectors中的summingInt方法,可以获取流中的数据的总和,并将最后的结果返回。
public static <T> Collector<T, ?, Integer>
summingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new int[1],
(a, t) -> { a[0] += mapper.applyAsInt(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
// 其中,ToIntFunction接口中有一个抽象方法
int applyAsInt(T value);
基本使用:
@Test
public void testStreamToOther() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 58, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
// 求总和
Integer sum = studentStream.collect(Collectors.summingInt(s -> s.getAge()));
System.out.println("总和: " + sum);
}
// 输出结果
总和:222
3.3 averagingInt
通过Collectors中的averagingInt方法,可以获取流中的数据的平均值,并将最后的结果返回。
public static <T> Collector<T, ?, Double>
averagingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new long[2],
(a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; },
(a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);
}
// 其中,ToIntFunction有一个抽象方法
int applyAsInt(T value);
基本使用:
@Test
public void testStreamToOther() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 58, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
// 平均值
Double avg = studentStream.collect(Collectors.averagingInt(Student::getSocre));
System.out.println("平均值: " + avg);
}
// 输出结果:
平均值: 89.75
3.4 counting
通过Collectors中的counting方法,可以获取流中的数据的总数,并将最后的结果返回。
public static <T> Collector<T, ?, Long>
counting() {
return reducing(0L, e -> 1L, Long::sum);
}
基本使用:
@Test
public void testStreamToOther() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 58, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
// 统计数量
Long count = studentStream.collect(Collectors.counting());
System.out.println("统计数量: " + count);
}
// 输出结果:
统计数量: 4
4. 对流中数据进行分组
当我们使用Stream流处理数据后,可以根据某个属性将数据分组。
基本使用:
@Test
public void testGroup() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 55),
new Student("柳岩", 52, 33));
// Map<Integer, List<Student>> map = studentStream.collect(Collectors.groupingBy(Student::getAge));
// map.forEach((key, value) -> System.out.println(key + "--->" +value));
// 将分数大于60的分为一组,小于60分成另一组
Map<String, List<Student>> map = studentStream.collect(Collectors.groupingBy((s) -> {
if (s.getSocre() > 60) {
return "及格";
} else {
return "不及格";
}
}));
map.forEach((k, v) -> {
System.out.println(k + "::" + v);
});
// 输出结果:
不及格::[Student{name='迪丽热巴', age=56, socre=55}, Student{name='柳岩', age=52, socre=33}]
及格::[Student{name='赵丽颖', age=52, socre=95}, Student{name='杨颖', age=56, socre=88}]
5. 对流中数据进行多级分组
还可以对数据进行多级分组。基本使用:
@Test
public void testCustomGroup() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 55),
new Student("柳岩", 52, 33));
// 先根据年龄分组,每组中在根据成绩分组
// groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
Map<Integer, Map<String, List<Student>>> map = studentStream.collect(Collectors.groupingBy(Student::getAge, Collectors.groupingBy((s) -> {
if (s.getSocre() > 60) {
return "及格";
} else {
return "不及格";
}
})));
// 遍历
map.forEach((k, v) -> {
System.out.println(k);
// v还是一个map,再次遍历
v.forEach((k2, v2) -> {
System.out.println("\t" + k2 + " == " + v2);
});
});
}
// 输出结果:
52
不及格 == [Student{name='柳岩', age=52, socre=33}]
及格 == [Student{name='赵丽颖', age=52, socre=95}]
56
不及格 == [Student{name='迪丽热巴', age=56, socre=55}]
及格 == [Student{name='杨颖', age=56, socre=88}]
6. 对流中的数据进行分区
Collectors.partitioningBy 会根据值是否为true,把集合分割为两个列表,一个true列表,一个false列表。基本使用:
@Test
public void testPartition() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 55),
new Student("柳岩", 52, 33));
Map<Boolean, List<Student>> map = studentStream.collect(Collectors.partitioningBy(s -> {
return s.getSocre() > 60;
}));
map.forEach((k, v) -> {
System.out.println(k + " :: " + v);
});
}
// 输出结果:
false :: [Student{name='迪丽热巴', age=56, socre=55}, Student{name='柳岩', age=52, socre=33}]
true :: [Student{name='赵丽颖', age=52, socre=95}, Student{name='杨颖', age=56, socre=88}]
7. 对流中的数据进行拼接
Collectors.joining 会根据指定的连接符,将所有元素连接成一个字符串。基本使用:
@Test
public void testJoining() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
// 根据一个字符串拼接: 赵丽颖__杨颖__迪丽热巴__柳岩
String names = studentStream.map(Student::getName).collect(Collectors.joining("__"));
// 根据三个字符串拼接
// String names = studentStream.map(Student::getName).collect(Collectors.joining("__", "^_^", "V_V"));
System.out.println("names = " + names);
}
// 输出结果:
names = 赵丽颖__杨颖__迪丽热巴__柳岩
names = ^_^赵丽颖__杨颖__迪丽热巴__柳岩V_V