Java8 Collectors详解

目录

1. averagingDouble

 2. collectingAndThen

3. counting

4. groupingBy

4.1 groupingBy(Function)

4.2 groupingBy(Function, Collector)

 4.3 groupingBy(Function, Supplier, Collector)

5. groupingByConcurrent

5.1 groupingByConcurrent(Function)

5.2 groupingByConcurrent(Function, Collector)

5.3 groupingByConcurrent(Function, Supplier, Collector) 

6. joining 

6.1 joining()

6.2 joining(delimiter)

6.3 joining(delimiter, prefix, suffix)

7. mapping

8. maxBy

9. minBy

10. partitioningBy

10.1 partitioningBy(Predicate)

10.2 partitioningBy(Predicate, Collector)

11. reducing

11.1 reducing(BinaryOperator)

11.2 reducing(Object,BinaryOperator)

11.3 reducing(Object,Function,BinaryOperator)

12. summarizingDouble

13. summingDouble

14. toCollection

15. toConcurrentMap

15.1 toConcurrentMap(Function, Function)

15.2 toConcurrentMap(Function, Function, BinaryOperator)

15.3 toConcurrentMap(Function, Function, BinaryOperator, Supplier)

16. toList

17. toMap

17.1 toMap(Function, Function)

17.2 toMap(Function, Function, BinaryOperator)

17.3 toMap(Function, Function, BinaryOperator, Supplier)

18. toSet


java.util.stream.Collectors实现各种有用的缩减操作的Collector的实现,例如将元素累积到集合中,根据各种标准汇总元素等。

以下以示例操作展示Collectors的详细用法,多数的操作是可以嵌套组合的,根据实际需要得到相应的结果。

/**
 * 学生信息
 */
public class Student {
    /** 姓名 */
    private String name;
    /** 总分 */
    private int totalScore;
    /** 是否本地人 */
    private boolean local;
    /** 年级 */
    private GradeType gradeType;

    /**
     * 年级类型
     */
    public enum GradeType {ONE,TWO,THREE}

    public Student(String name, int totalScore, boolean local, GradeType gradeType) {
        this.name = name;
        this.totalScore = totalScore;
        this.local = local;
        this.gradeType = gradeType;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", totalScore=" + totalScore +
                ", local=" + local +
                ", gradeType=" + gradeType +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getTotalScore() {
        return totalScore;
    }

    public void setTotalScore(int totalScore) {
        this.totalScore = totalScore;
    }

    public boolean isLocal() {
        return local;
    }

    public void setLocal(boolean local) {
        this.local = local;
    }

    public GradeType getGradeType() {
        return gradeType;
    }

    public void setGradeType(GradeType gradeType) {
        this.gradeType = gradeType;
    }
}

 初始化测试数据:

List<Student> menu = Arrays.asList(
    new Student("刘一", 721, true, Student.GradeType.THREE),
    new Student("陈二", 637, true, Student.GradeType.THREE),
    new Student("张三", 666, true, Student.GradeType.THREE),
    new Student("李四", 531, true, Student.GradeType.TWO),
    new Student("王五", 483, false, Student.GradeType.THREE),
    new Student("赵六", 367, true, Student.GradeType.THREE),
    new Student("孙七", 499, false, Student.GradeType.ONE));
);

1. averagingDouble

averagingDouble方法返回一个Collector收集器,它生成应用于输入元素的double值函数的算术平均值。如果没有元素,则结果为0。

返回的平均值可能会因记录值的顺序而变化,这是由于除了不同大小的值之外,还存在累积舍入误差。通过增加绝对量排序的值(即总量,样本越大,结果越准确)往往会产生更准确的结果。如果任何记录的值是NaN或者总和在任何点NaN,那么平均值将是NaN。

注意: double格式可以表示-253到253范围内的所有连续整数。如果管道有超过253的值,则平均计算中的除数将在253处饱和,从而导致额外的数值误差。

示例:统计所有学生的平均总成绩

@Test
public void testAveragingDouble() {
    Double averagingDouble = menu.stream().collect(Collectors.averagingDouble(Student::getTotalScore));
    Optional.ofNullable(averagingDouble).ifPresent(System.out::println); 
}
// 557.7142857142857

 2. collectingAndThen

collectingAndThen方法调整Collector收集器以执行其它的结束转换。例如,可以调整toList()收集器,以始终生成一个不可变的列表:

List<Student> studentList = menu.stream().collect(
    Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
System.out.println(studentList);

示例:以指定字符串The Average totalScore is->输出所有学生的平均总成绩

@Test
public void testCollectingAndThen() {
    Optional.ofNullable(menu.stream().collect(
        Collectors.collectingAndThen(
            Collectors.averagingInt(Student::getTotalScore), a -> "The Average totalScore is->" + a)
    )).ifPresent(System.out::println);
}
// The Average totalScore is->557.7142857142857

3. counting

counting方法返回一个Collector收集器接受T类型的元素,用于计算输入元素的数量。如果没有元素,则结果为0。

示例:统计所有学生人数

@Test
public void testCounting() {
    Optional.of(menu.stream().collect(Collectors.counting())).ifPresent(System.out::println);
}
// 7

4. groupingBy

4.1 groupingBy(Function)

groupingBy(Function)方法返回一个Collector收集器对T类型的输入元素执行"group by"操作,根据分类函数对元素进行分组,并将结果返回到Map。

分类函数将元素映射到某些键类型K。收集器生成一个Map<K, List<T>>,其键是将分类函数应用于输入元素得到的值,其对应值为List,其中包含映射到分类函数下关联键的输入元素。
无法保证返回的Map或List对象的类型,可变性,可序列化或线程安全性。

注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。如果不需要保留元素出现在生成的Map收集器中的顺序,则使用groupingByConcurrent(Function)可以提供更好的并行性能。

 示例:统计各个年级的学生信息

@Test
public void testGroupingByFunction() {
    Map<Student.GradeType, List<Student>> collect = menu.stream()
        .collect(Collectors.groupingBy(Student::getGradeType));

    Optional.ofNullable(collect).ifPresent(System.out::println);
}

------------------
{TWO=[Student{name='李四', totalScore=531, local=true, gradeType=TWO}],
THREE=[Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='张三', totalScore=666, local=true, gradeType=THREE}, Student{name='王五', totalScore=483, local=false, gradeType=THREE}, Student{name='赵六', totalScore=367, local=true, gradeType=THREE}], 
ONE=[Student{name='孙七', totalScore=499, local=false, gradeType=ONE}]}

4.2 groupingBy(Function, Collector)

groupingBy(Function, Collector)方法返回一个Collector收集器,对T类型的输入元素执行级联"group by"操作,根据分类函数对元素进行分组,然后使用指定的下游Collector收集器对与给定键关联的值执行缩减操作。

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map<K, D>。
返回的Map的类型,可变性,可序列化或线程安全性无法保证。

注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。如果不需要保留向下游收集器提供元素的顺序,则使用groupingByConcurrent(Function, Collector)可以提供更好的并行性能。

 示例:统计各个年级的学生人数

@Test
public void testGroupingByFunctionAndCollector() {
    Optional.of(menu.stream()
    	.collect(Collectors.groupingBy(Student::getGradeType, Collectors.counting())))
        .ifPresent(System.out::println);
}
// {THREE=5, ONE=1, TWO=1}

 4.3 groupingBy(Function, Supplier, Collector)

groupingBy(Function, Supplier, Collector)方法返回一个Collector收集器,对T类型的输入元素执行级联"group by"操作,根据分类函数对元素进行分组,然后使用指定的下游Collector收集器对与给定键关联的值执行缩减操作。收集器生成的Map是使用提供的工厂函数创建的。

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map<K, D>。

注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。如果不需要保留向下游收集器提供元素的顺序,则使用groupingByConcurrent(Function, Supplier, Collector)可以提供更好的并行性能。

示例:统计各个年级的平均成绩,并有序输出

@Test
public void testGroupingByFunctionAndSupplierAndCollector() {
    Map<Student.GradeType, Double> map = menu.stream()
        .collect(Collectors.groupingBy(
            Student::getGradeType,
            TreeMap::new,
            Collectors.averagingInt(Student::getTotalScore)));

    Optional.of(map.getClass()).ifPresent(System.out::println);
    Optional.of(map).ifPresent(System.out::println);
}
// class java.util.TreeMap
// {ONE=499.0, TWO=531.0, THREE=574.8}

注意这里的Supplier使用的是TreeMap,TreeMap是有序的。

5. groupingByConcurrent

5.1 groupingByConcurrent(Function)

groupingByConcurrent(Function)方法返回一个并发Collector收集器对T类型的输入元素执行"group by"操作,根据分类函数对元素进行分组

 这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。
分类函数将元素映射到某些键类型K。收集器生成一个ConcurrentMap<K, List>,其键是将分类函数应用于输入元素得到的值,其对应值为List,其中包含映射到分类函数下关联键的输入元素。
无法保证返回的Map或List对象的类型,可变性或可序列化,或者返回的List对象的线程安全性。

5.2 groupingByConcurrent(Function, Collector)

groupingByConcurrent(Function, Collector)方法返回一个并发Collector收集器,对T类型的输入元素执行级联"group by"操作,根据分类函数对元素进行分组,然后使用指定的下游Collector收集器对与给定键关联的值执行缩减操作。

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map<K, D>。

5.3 groupingByConcurrent(Function, Supplier, Collector) 

groupingByConcurrent(Function, Supplier, Collector)方法返回一个并行Collector收集器,对T类型的输入元素执行级联"group by"操作,根据分类函数对元素进行分组,然后使用指定的下游Collector收集器对与给定键关联的值执行缩减操作。收集器生成的ConcurrentMap是使用提供的工厂函数创建的。

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map<K, D>。

6. joining 

6.1 joining()

joining()方法返回一个Collector收集器,它按遇见顺序将输入元素连接成String。

示例:将所有学生的姓名连接成字符串

@Test
public void testJoining() {
    Optional.of(menu.stream().map(Student::getName).collect(Collectors.joining()))
        .ifPresent(System.out::println);
}
// 刘一陈二张三李四王五赵六孙七

6.2 joining(delimiter)

joining(delimiter)方法返回一个Collector收集器,它以遇见顺序连接由指定分隔符分隔的输入元素。

示例:将所有学生的姓名以","分隔连接成字符串

@Test
public void testJoiningWithDelimiter() {
    Optional.of(menu.stream().map(Student::getName).collect(Collectors.joining(",")))
        .ifPresent(System.out::println);
}
// 刘一,陈二,张三,李四,王五,赵六,孙七

6.3 joining(delimiter, prefix, suffix)

joining(delimiter, prefix, suffix)方法返回一个Collector收集器,它以遇见顺序将由指定分隔符分隔的输入元素与指定的前缀和后缀连接起来。

示例:将所有学生的姓名以","分隔,以Names[为前缀,]为后缀连接成字符串

@Test
public void testJoiningWithDelimiterAndPrefixAndSuffix() {
    Optional.of(menu.stream().map(Student::getName).collect(Collectors.joining(",", "Names[", "]")))
        .ifPresent(System.out::println);
}
// Names[刘一,陈二,张三,李四,王五,赵六,孙七]

7. mapping

mapping方法通过在累积之前将映射函数应用于每个输入元素,将Collector收集器接受U类型的元素调整为一个接受T类型的元素。

示例:将所有学生的姓名以","分隔连接成字符串

@Test
public void testMapping() {
    Optional.of(menu.stream().collect(Collectors.mapping(Student::getName, Collectors.joining(","))))
        .ifPresent(System.out::println);
}
// 刘一,陈二,张三,李四,王五,赵六,孙七

8. maxBy

maxBy方法返回一个Collector收集器,它根据给定的Comparator比较器生成最大元素,描述为Optional<T>

示例:列出所有学生中成绩最高的学生信息

@Test
public void testMaxBy() {
    menu.stream().collect(Collectors.maxBy(Comparator.comparingInt(Student::getTotalScore)))
        .ifPresent(System.out::println);
}
// Student{name='刘一', totalScore=721, local=true, gradeType=THREE}

9. minBy

minBy方法返回一个Collector收集器,它根据给定的Comparator比较器生成最小元素,描述为Optional<T>

示例:列出所有学生中成绩最低的学生信息

@Test
public void testMinBy() {
    menu.stream().collect(Collectors.minBy(Comparator.comparingInt(Student::getTotalScore)))
        .ifPresent(System.out::println);
}
// Student{name='赵六', totalScore=367, local=true, gradeType=THREE}

10. partitioningBy

10.1 partitioningBy(Predicate)

partitioningBy(Predicate)方法返回一个Collector收集器,它根据Predicate对输入元素进行分区,并将它们组织成Map<Boolean, List<T>>

 返回的Map的类型,可变性,可序列化或线程安全性无法保证。

示例:列出所有学生中本地和非本地学生信息 

@Test
public void testPartitioningByWithPredicate() {
    Map<Boolean, List<Student>> collect = menu.stream()
    	.collect(Collectors.partitioningBy(Student::isLocal));
    Optional.of(collect).ifPresent(System.out::println);
}
// {false=[Student{name='王五', totalScore=483, local=false, gradeType=THREE}, Student{name='孙七', totalScore=499, local=false, gradeType=ONE}], true=[Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='张三', totalScore=666, local=true, gradeType=THREE}, Student{name='李四', totalScore=531, local=true, gradeType=TWO}, Student{name='赵六', totalScore=367, local=true, gradeType=THREE}]}

10.2 partitioningBy(Predicate, Collector)

partitioningBy(Predicate, Collector)方法返回一个Collector收集器,它根据Predicate对输入元素进行分区,根据另一个Collector收集器减少每个分区中的值,并将它们组织成Map<Boolean, D>,其值是下游减少的结果。 

示例:列出所有学生中本地和非本地学生的平均总成绩

@Test
public void testPartitioningByWithPredicateAndCollector() {
    Map<Boolean, Double> collect = menu.stream()
        .collect(Collectors.partitioningBy(
        			Student::isLocal, 
        			Collectors.averagingInt(Student::getTotalScore)));
    Optional.of(collect).ifPresent(System.out::println);
}
// {false=491.0, true=584.4}

11. reducing

11.1 reducing(BinaryOperator)

返回一个Collector收集器,它在指定的BinaryOperator下执行其输入元素的缩减。结果被描述为Optional<T>

 reducing()相关收集器在groupingBy或partitioningBy下游的多级缩减中使用时非常有用。要对流执行简单缩减,请使用Stream#reduce(BinaryOperator)。

示例:列出所有学生中成绩最高的学生信息

@Test
public void testReducingBinaryOperator() {
    menu.stream().collect(Collectors
                          .reducing(BinaryOperator
                                    .maxBy(Comparator
                                           .comparingInt(Student::getTotalScore))))
        .ifPresent(System.out::println);
}
// Student{name='刘一', totalScore=721, local=true, gradeType=THREE}

可直接使用reduce

@Test
public void testReducingBinaryOperator() {
    menu.stream().reduce(BinaryOperator
                         .maxBy(Comparator
                                .comparingInt(Student::getTotalScore)))
        .ifPresent(System.out::println);
}
// Student{name='刘一', totalScore=721, local=true, gradeType=THREE}

11.2 reducing(Object,BinaryOperator)

返回一个Collector收集器,它使用提供的标识在指定的BinaryOperator下执行其输入元素的缩减。

reducing()相关收集器在groupingBy或partitioningBy下游的多级缩减中使用时非常有用。要对流执行简单缩减,请使用Stream#reduce(Object, BinaryOperator)。

示例:统计所有学生的总成绩

@Test
public void testReducingBinaryOperatorAndIdentiy() {
    Integer result = menu.stream()
        .map(Student::getTotalScore)
        .collect(Collectors.reducing(0, (d1, d2) -> d1 + d2));
    System.out.println(result);
}
// 3904

可直接使用reduce

@Test
public void testReducingBinaryOperatorAndIdentiy() {
    Integer result = menu.stream()
        .map(Student::getTotalScore).reduce(0, (d1, d2) -> d1 + d2);
    System.out.println(result);
}

11.3 reducing(Object,Function,BinaryOperator)

返回一个Collector收集器,它在指定的映射函数和BinaryOperator下执行其输入元素的缩减。这是对reducing(Object, BinaryOperator)的概括,它允许在缩减之前转换元素。

reducing()相关收集器在groupingBy或partitioningBy下游的多级缩减中使用时非常有用。要对流执行简单缩减,请使用Stream#map(Function)和Stream#reduce(Object, BinaryOperator)。

示例:统计所有学生的总成绩

@Test
public void testReducingBinaryOperatorAndIdentiyAndFunction() {
    Integer result = menu.stream()
        .collect(Collectors.reducing(0, Student::getTotalScore, (d1, d2) -> d1 + d2));
    System.out.println(result);
}
// 3904

可直接使用reduce

@Test
public void testReducingBinaryOperatorAndIdentiyAndFunction() {
    Integer result = menu.stream()
        .map(Student::getTotalScore)
        .reduce(0, (d1, d2) -> d1 + d2);
    System.out.println(result);
}

12. summarizingDouble

summarizingDouble方法返回一个Collector收集器,它将double生成映射函数应用于每个输入元素,并返回结果值的摘要统计信息。

示例:统计所有学生的摘要信息(总人数,总成绩,最高成绩,最低成绩和平均成绩)

@Test
public void testSummarizingInt() {
    DoubleSummaryStatistics result = menu.stream()
            .collect(Collectors.summarizingDouble(Student::getTotalScore));
    Optional.of(result).ifPresent(System.out::println);
}
// DoubleSummaryStatistics{count=7, sum=3904.000000, min=367.000000, average=557.714286, max=721.000000}
// IntSummaryStatistics{count=7, sum=3904, min=367, average=557.714286, max=721}
// LongSummaryStatistics{count=7, sum=3904, min=367, average=557.714286, max=721}

13. summingDouble

返回一个Collector收集器,它生成应用于输入元素的double值函数的总和。如果没有元素,则结果为0。

返回的总和可能会因记录值的顺序而变化,这是由于除了不同大小的值之外,还存在累积舍入误差。通过增加绝对量排序的值(即总量,样本越大,结果越准确)往往会产生更准确的结果。如果任何记录的值是NaN或者总和在任何点NaN,那么总和将是NaN。

示例:统计所有学生的总成绩

@Test
public void testSummingDouble() {
    Optional.of(menu.stream().collect(Collectors.summingDouble(Student::getTotalScore)))
        .ifPresent(System.out::println);
}
// summingDouble 3904.0
// summingInt 3904
// summingLong 3904

14. toCollection

返回一个Collector收集器,它按遇见顺序将输入元素累积到一个新的Collection收集器中。Collection收集器由提供的工厂创建。

示例:统计总分大于600的所有学生的信息放入LinkedList中

@Test
public void testToCollection() {
    Optional.of(menu.stream().filter(d -> d.getTotalScore() > 600)
                .collect(Collectors.toCollection(LinkedList::new)))
        .ifPresent(v -> {
      	    System.out.println(v.getClass());
            System.out.println(v);
         });
}
// class java.util.LinkedList
// [Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='张三', totalScore=666, local=true, gradeType=THREE}]

15. toConcurrentMap

15.1 toConcurrentMap(Function, Function)

返回一个并发的Collector收集器,它将元素累积到ConcurrentMap中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则在执行收集操作时会抛出IllegalStateException。如果映射的键可能有重复,请使用toConcurrentMap(Function, Function, BinaryOperator)。

注意: 键或值作为输入元素是常见的。在这种情况下,实用方法java.util.function.Function#identity()可能会有所帮助。

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

示例:以学生姓名为键总分为值统计信息 

@Test
public void testToConcurrentMap() {
    Optional.of(menu.stream()
                .collect(Collectors.toConcurrentMap(Student::getName, Student::getTotalScore)))
        .ifPresent(v -> {
            System.out.println(v);
            System.out.println(v.getClass());
        });
}
// {李四=531, 孙七=499, 刘一=721, 张三=666, 陈二=637, 王五=483, 赵六=367}
// class java.util.concurrent.ConcurrentHashMap

15.2 toConcurrentMap(Function, Function, BinaryOperator)

返回一个并发的Collector收集器,它将元素累积到ConcurrentMap中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。

注意: 有多种方法可以处理映射到同一个键的多个元素之间的冲突。toConcurrentMap的其它形式只是使用无条件抛出的合并函数,但你可以轻松编写更灵活的合并策略。例如,如果你有一个Person流,并且你希望生成一个“电话簿”映射名称到地址,但可能有两个人具有相同的名称,你可以按照以下方式进行优雅的处理这些冲突,并生成一个Map将名称映射到连接的地址列表中:

Map<String, String> phoneBook
    people.stream().collect(toConcurrentMap(Person::getName,
                                            Person::getAddress,
                                            (s, a) -> s + ", " + a));

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

示例:以年级为键学生人数为值统计信息

/**
 * GradeType:TotalNum
 */
@Test
public void testToConcurrentMapWithBinaryOperator() {
    Optional.of(menu.stream()
                .collect(Collectors.toConcurrentMap(Student::getGradeType, v -> 1L, (a, b) -> a + b)))
        .ifPresent(v -> {
            System.out.println(v);
            System.out.println(v.getClass());
        });
}
// {ONE=1, THREE=5, TWO=1}
// class java.util.concurrent.ConcurrentHashMap

15.3 toConcurrentMap(Function, Function, BinaryOperator, Supplier)

返回一个并发的Collector收集器,它将元素累积到ConcurrentMap中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。ConcurrentMap由提供的供应商函数创建。

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

示例:以年级为键学生人数为值将统计的信息放入ConcurrentSkipListMap中 

/**
 * GradeType:TotalNum
 */
@Test
public void testToConcurrentMapWithBinaryOperatorAndSupplier() {
    Optional.of(menu.stream()
            .collect(Collectors.toConcurrentMap(
		            Student::getGradeType, 
		            v -> 1L, 
		            (a, b) -> a + b, 
		            ConcurrentSkipListMap::new)))
            .ifPresent(v -> {
                System.out.println(v);
                System.out.println(v.getClass());
            });
}
// {ONE=1, TWO=1, THREE=5}
// class java.util.concurrent.ConcurrentSkipListMap

16. toList

返回一个Collector收集器,它将输入元素累积到一个新的List中。返回的List的类型,可变性,可序列化或线程安全性无法保证;如果需要更多地控制返回的List,请使用toCollection(Supplier)。

示例:查出本地学生的信息并放入ArrayList中

@Test
public void testToList() {
    Optional.of(menu.stream().filter(Student::isLocal).collect(Collectors.toList()))
        .ifPresent(r -> {
            System.out.println(r.getClass());
            System.out.println(r);
        });
}
// class java.util.ArrayList
// [Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='张三', totalScore=666, local=true, gradeType=THREE}, Student{name='李四', totalScore=531, local=true, gradeType=TWO}, Student{name='赵六', totalScore=367, local=true, gradeType=THREE}]

17. toMap

17.1 toMap(Function, Function)

返回一个Collector收集器,它将元素累积到Map中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则在执行收集操作时会抛出IllegalStateException如果映射的键可能有重复,请使用toMap(Function, Function, BinaryOperator)。

注意: 键或值作为输入元素是常见的。在这种情况下,实用方法java.util.function.Function#identity()可能会有所帮助。

返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。

如果不需要将结果以遇见的顺序插入Map,则使用toConcurrentMap(Function, Function)可以提供更好的并行性能。

17.2 toMap(Function, Function, BinaryOperator)

返回一个并发的Collector收集器,它将元素累积到Map中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。

注意: 有多种方法可以处理映射到同一个键的多个元素之间的冲突。toMap的其它形式只是使用无条件抛出的合并函数,但你可以轻松编写更灵活的合并策略。例如,如果你有一个Person流,并且你希望生成一个“电话簿”映射名称到地址,但可能有两个人具有相同的名称,你可以按照以下方式进行优雅的处理这些冲突,并生成一个Map将名称映射到连接的地址列表中:

Map<String, String> phoneBook
 people.stream().collect(toConcurrentMap(Person::getName,
                                         Person::getAddress,
                                         (s, a) -> s + ", " + a));

返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。

如果不需要将结果以遇见的顺序插入Map,则使用toConcurrentMap(Function, Function, BinaryOperator)可以提供更好的并行性能。

案例:新建一个Sdudent 类,三个属性分别是id,name,group

1. 将list转成以id为key的map,value是id对应的Sudent对象: 

Map<String, Student> map = list.stream().collect(Collectors.toMap(Student::getId, Function.identity()));

拓展:Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法,identity()就是Function接口的一个静态方法。
Function.identity()返回一个输出跟输入一样的Lambda表达式对象,等价于形如t -> t形式的Lambda表达式。

static  Function identity() {
    return t -> t;
}

2.假如id存在重复值,则会报错Duplicate key xxx, 解决方案是:

只取后一个key及value

Map<String, Student> map = list.stream().collect(Collectors.toMap(Student::getId,Function.identity(),(oldValue,newValue) -> newValue))

只取前一个key及value

Map<String, Student> map = list.stream().collect(Collectors.toMap(Student::getId,Function.identity(),(oldValue,newValue) -> oldValue))

3.想获得一个id和name对应的Map<String, String> :

Map<String, String> map = list.stream().collect(Collectors.toMap(Student::getId,Student::getName)); 

注意:name可以为空字符串但不能为null,否则会报空指针,解决方案:

Map<String, String> map = list.stream().collect(Collectors.toMap(Student::getId, e->e.getName()==null?"":e.getName()));

17.3 toMap(Function, Function, BinaryOperator, Supplier)

返回一个并发的Collector收集器,它将元素累积到Map中,其键和值是将提供的映射函数应用于输入元素的结果。

 如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。Map由提供的供应商函数创建。

注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。

如果不需要将结果以遇见的顺序插入Map,则使用toConcurrentMap(Function, Function, BinaryOperator, Supplier)可以提供更好的并行性能。

18. toSet

返回一个Collector收集器,它将输入元素累积到一个新的Set中。返回的Set的类型,可变性,可序列化或线程安全性无法保证;如果需要更多地控制返回的Set,请使用toCollection(Supplier)。 

这是一个Collector.Characteristics#UNORDERED无序收集器。 

示例:查出本地学生的信息并放入HashSet中 

@Test
public void testToSet() {
    Optional.of(menu.stream().filter(Student::isLocal).collect(Collectors.toSet()))
        .ifPresent(r -> {
            System.out.println(r.getClass());
            System.out.println(r);
        });
}
// class java.util.HashSet
// [Student{name='张三', totalScore=666, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='李四', totalScore=531, local=true, gradeType=TWO}, Student{name='赵六', totalScore=367, local=true, gradeType=THREE}]

本文参考:
Collectors (Java Platform SE 8 )

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java 8中的Collectors.toMap方法可以将Stream中的元素转换为一个Map对象。该方法有三个参数:keyMapper,valueMapper和mergeFunction。其中,keyMapper用于将Stream中的元素转换为Map的key,valueMapper用于将Stream中的元素转换为Map的value,mergeFunction用于处理当key重复时的冲突情况。 下面是一个例子,假设我们有一个List对象,其中包含多个Person对象,每个Person对象都有一个唯一的id属性和一个name属性。我们可以使用Collectors.toMap方法将List转换为一个以id为key,以Person对象为value的Map对象: ```java List<Person> personList = new ArrayList<>(); // 假设我们已经将多个Person对象添加到了personList中 Map<Integer, Person> personMap = personList.stream() .collect(Collectors.toMap(Person::getId, Function.identity())); ``` 在上面的例子中,Person::getId表示将Person对象的id属性作为Map的key,Function.identity()表示将Person对象本身作为Map的value。如果我们想要将Person对象的name属性作为Map的value,可以这样写: ```java Map<Integer, String> personMap = personList.stream() .collect(Collectors.toMap(Person::getId, Person::getName)); ``` 如果我们的List中有重复的id,那么上面的代码将会抛出IllegalStateException异常。为了避免这种情况,我们可以使用mergeFunction参数来处理冲突。例如,如果我们想要将重复的id的Person对象合并为一个List,可以这样写: ```java Map<Integer, List<Person>> personMap = personList.stream() .collect(Collectors.toMap(Person::getId, Collections::singletonList, (list1, list2) -> { List<Person> resultList = new ArrayList<>(list1); resultList.addAll(list2); return resultList; })); ``` 在上面的代码中,Collections::singletonList表示将Person对象转换为只包含一个元素的List,mergeFunction参数则表示将两个List合并为一个List。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值