java8 流中的Collectors

阅读数:127 评论数:0

跳转到新版页面

分类

python/Java

正文

一、概述

Collectors是java.util.stream包下的一个工具类,其中各个方法的返回值可以作为java.util.stream.Stream#collect的入参,实现对队列的各种操作。

二、常用方法

1、counting

统计聚合结果的元素数量

students.stream().collect(Collectors.counting())

2、平均值 averagingDouble、averagingInt、averagingLong

// 11.0
students.stream().collect(Collectors.averagingInt(Student::getAge))
// 11.0
students.stream().collect(Collectors.averagingDouble(Student::getAge))
// 11.0
students.stream().collect(Collectors.averagingLong(Student::getAge))

3、求和: summingDouble、summingInt、summingLong

// 66
students.stream().collect(Collectors.summingInt(s -> (int)s.getScore()))
// 66.369
students.stream().collect(Collectors.summingDouble(Student::getScore))
// 66
students.stream().collect(Collectors.summingLong(s -> (long)s.getScore()))

4、最大值、最小值元素: maxBy、minBy

// Optional[Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)],注意返回类型是Optional
students.stream().collect(Collectors.minBy(Comparator.comparing(Student::getAge)))
// Optional[Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123)],注意返回类型是Optional
students.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)))

5、统计结果:summarizingDouble、summarizingInt、summarizingLong

计数、求平均、求和、最大、最小

// IntSummaryStatistics{count=3, sum=66, min=12, average=22.000000, max=32}
students.stream().collect(Collectors.summarizingInt(s -> (int) s.getScore()))
// DoubleSummaryStatistics{count=3, sum=66.369000, min=12.123000, average=22.123000, max=32.123000}
students.stream().collect(Collectors.summarizingDouble(Student::getScore))
// LongSummaryStatistics{count=3, sum=66, min=12, average=22.000000, max=32}
students.stream().collect(Collectors.summarizingLong(s -> (long) s.getScore()))

6、聚合元素:toList、toSet、toCollection

// List: [1, 2, 3]
final List<String> idList = students.stream().map(Student::getId).collect(Collectors.toList());
// Set: [1, 2, 3]
final Set<String> idSet = students.stream().map(Student::getId).collect(Collectors.toSet());
// TreeSet: [1, 2, 3]
final Collection<String> idTreeSet = students.stream().map(Student::getId).collect(Collectors.toCollection(TreeSet::new));

7、聚合元素:toMap、toConcurrentMap

// {1=Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123), 2=Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123), 3=Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)}
final Map<String, Student> map11 = students.stream()
    .collect(Collectors.toMap(Student::getId, Function.identity()));

如果 id 有重复的,会抛出java.lang.IllegalStateException: Duplicate key异常,所以,为了保险起见,我们需要借助toMap另一个重载方法

// {1=Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123), 2=Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123), 3=Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)}
final Map<String, Student> map2 = students.stream()
    .collect(Collectors.toMap(Student::getId, Function.identity(), (x, y) -> x));

toMap有不同的重载方法,可以实现比较复杂的逻辑。

比如,我们需要得到根据 id 分组的Student的姓名:

// {1=张三, 2=李四, 3=王五}
final Map<String, String> map3 = students.stream()
    .collect(Collectors.toMap(Student::getId, Student::getName, (x, y) -> x));

比如,我们需要得到相同年龄得分最高的Student对象集合

// {10=Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123), 11=Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123), 12=Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123)}
final Map<Integer, Student> map5 = students.stream()
    .collect(Collectors.toMap(Student::getAge, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(Student::getScore))));

8、分组:groupingBy、groupingByConcurrent

groupingBy与toMap都是将聚合元素进行分组,区别是,toMap结果是 1:1 的 k-v 结构,groupingBy的结果是 1:n 的 k-v 结构。

如果想要线程安全的Map,可以使用groupingByConcurrent。

// List: {10=[Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)], 11=[Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123)], 12=[Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123)]}
final Map<Integer, List<Student>> map1 = students.stream().collect(Collectors.groupingBy(Student::getAge));
// Set: {10=[Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)], 11=[Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123)], 12=[Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123)]}
final Map<Integer, Set<Student>> map12 = students.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.toSet()));

9、分组partitioningBy

partitioningBy与groupingBy的区别在于,partitioningBy借助Predicate断言,可以将集合元素分为true和false两部分

// List: {false=[Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123), Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)], true=[Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123)]}
final Map<Boolean, List<Student>> map6 = students.stream().collect(Collectors.partitioningBy(s -> s.getAge() > 11));
// Set: {false=[Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123), Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123)], true=[Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123)]}
final Map<Boolean, Set<Student>> map7 = students.stream().collect(Collectors.partitioningBy(s -> s.getAge() > 11, Collectors.toSet()));

10、链接数据 joining

这个方法对String类型的元素进行聚合,拼接成一个字符串返回,作用与java.lang.String#join类似

// javagosql
Stream.of("java", "go", "sql").collect(Collectors.joining());
// java, go, sql
Stream.of("java", "go", "sql").collect(Collectors.joining(", "));
// 【java, go, sql】
Stream.of("java", "go", "sql").collect(Collectors.joining(", ", "【", "】"));

11、操作链 collectingAndThen

它是先对集合进行一次聚合操作,然后通过Function定义的函数,对聚合后的结果再次处理。

// {1=Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123), 2=Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123), 3=Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)}
final Map<String, Student> map3 = students.stream()
    .collect(Collectors.groupingBy(Student::getId, Collectors.collectingAndThen(Collectors.toList(), list -> list.get(0))));

显示将结果聚合成List列表,然后取列表的第 0 个元素返回

12、操作后聚合 mapping

mapping先通过Function函数处理数据,然后通过Collector方法聚合元素

// [张三, 李四, 王五]
students.stream()
        .collect(Collectors.mapping(Student::getName, Collectors.toList()));

13、聚合后操作reducing

//:直接通过BinaryOperator操作,返回值是Optional
public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op);
//预定默认值,然后通过BinaryOperator操作
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op);
//预定默认值,通过Function操作元素,然后通过BinaryOperator操作
public static <T, U> Collector<T, ?, U> reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op);
// Optional[66.369],注意返回类型是Optional
students.stream()
        .map(Student::getScore)
        .collect(Collectors.reducing(Double::sum));
// 66.369
students.stream()
        .map(Student::getScore)
        .collect(Collectors.reducing(0.0, Double::sum));
// 66.369
students.stream()
        .collect(Collectors.reducing(0.0, Student::getScore, Double::sum));

 




相关推荐

修饰接口中默认的方法实现(也就是接口中可以包含方法体,这打破了Java之前版本对接口的语法限制)。 public int

一、概述 1、lambda允许把函数作为一个方法的参数。 二、取代某些匿名内部类 1、无参数函数的简写 如果需要创建一个线程,一种常见的写法是这样: // JDK7 匿名内部类写法 new Threa

作用:使代码更简洁。 有些情况下,我们用lambda表达式仅仅是调用一些已经存在的方法,除了调用动作外,没有其他任何多余的动作,而通过方法引用可以使代码更简洁、更容易理解。 类型 语法 对应的L

一、概述 如果没有终端操作,中间操作是不会得到执行的(惰性求值)。 stream的使用可以将代码中大量的for循环变为简单的链式函数操作,但是需要注意性能,在数据量小的情况下二都相差不多,但是在数据

在JAVA5已经提供了Future和Callable的实现,可以用于阻塞式获取结果,如果想要异步获取结果,通常都会以轮询的方式去获取结果,如下: <pre class="language

Supplier接口是JAVA8以后配合lambda表达式和函数式接口编程(FunctionInterface,以下简称FI)组合使用的一个接口。它最适合于表示工厂,简而言之,Supplier&

一、函数式编程和非函数编程的区别 (1)函数式编程:先考虑传入的参数,再考虑方法的实现。 (2)非函数编程:先定义好方法,再传入指定的参数 二、何为函数式接口 函数式接口(Functional Int

java8时,java.util.concurrent.atomic包中提供了一个新的原子类LongAddr,提供了原子累计值的方法,在高并发的场景下它比Ato

一、Java8简介 1、添加了函数式编程、Stream、全新的日期处理类。 2、函数编程新加了一些概念:lambda表达式、函数式接口、函数引用、默认方法、Optional类(干掉空指针)等。 3、S

一、概述 Java8引入了@Contented这个新的注解来减少伪共享(False Sharing)的发生。 二、伪共享 1、缓存行 CPU读取内存数据时并非一次只读一个字节,而是会读一段64字节长度