Java8学习笔记(十)--自定义收集器

前言

之前写过Java8中的自定义收集器,当时只是在文章末尾放了个例子,以为基本用法挺简单,并且有些东西没搞懂(好比combiner方法到底作什么的),没有专门写,过了一段时间又忘了,因此,即便仍是没搞懂combiner方法,仍是硬着头皮把使用的经验记录下,方便之后参考。java

简介

要实现自定义收集器,只须要实现java.util.stream.Collector<T, A, R>接口便可,这个接口包含五个无参方法:[supplier, accumulator, combiner, finisher, characteristics]。json

泛型简介

泛型含义以下:数据结构

  • T:缩减操做的输入元素的类型
  • A:还原操做的可变累积类型(一般隐藏为实现细节)
  • R:还原操做的结果类型

T没必要说,收集什么泛型的列表,就输入什么类型,好比我对一个Student列表进行收集计算,那么T确定是Student。
A是计算过程当中用来盛放计算结果的容器,通常都是List,Set等等。
R就比较好理解,就是收集完成后返回的类型,须要注意的是,当characteristics()中包含Characteristics.IDENTITY_FINISH时,。并发

方法简介

  • characteristics 表示收集计算的方式,返回类型为Set<Characteristics>,其中Characteristics是一个枚举类型,指示收集器属性的特征,可用于优化缩减实现。它只有三个值[CONCURRENT, UNORDERED, IDENTITY_FINISH],注释翻译过来分别是:1.表示此收集器是并发的,这意味着结果容器能够支持与来自多个线程的相同结果容器同时调用的累加器函数。若是CONCURRENT收集器也不是UNORDERED,那么只有在应用于无序数据源时才应同时评估它。2.指示集合操做不承诺保留输入元素的遭遇顺序。(若是结果容器没有内在顺序,例如Set,则多是这样。)3.表示整理器功能是标识功能,能够省略。 若是设置,则必须是从A到R的未经检查的强制转换成功的状况。
  • supplier 该方法返回一个Supplier<A>类型的结果,表示在计算过程当中,如何初始化一个临时容器,好比A=List,那么通常返回ArrayList::new
  • accumulator 核心方法,关键的计算逻辑都放在这里,定义了如何把一个个元素放入临时容器中,返回类型为BiConsumer<A, T>
  • combiner 返回一个BinaryOperator<A>类型的结果,我的理解是如何合并临时容器,可是在实际应用中没碰到执行过,因此通常直接返回null
  • finisher 定义了如何把临时容器转换为输出结果,返回类型为Function<A, R>,须要注意的是,当方法characteristics的返回值中包含Characteristics.IDENTITY_FINISH,则必须保证从A到R可以强制转换成功,此时该方法固定返回Function.identity()便可。不然会报错。好比A为ArrayList,而R为HashMap的状况就会报错。

实战

添加依赖

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.33</version>
        </dependency>

需求1

有一个学生列表,求计算学生的总分ide

数据结构

public class Student {
    private String id;
    private Course course;
    private double score;

    public Student() {
    }

    public Student(String id, Course course, double score) {
        this.id = id;
        this.course = course;
        this.score = score;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this, true);
    }

    public enum Course {
        LANGUAGE, MATHEMATICS, ENGLISH, TOTAL
    }

    // 省略setter、getter方法
}

列表定义

public static final List<Student> students =
            List.of(new Student("1", Student.Course.ENGLISH, 78), new Student("1", Student.Course.LANGUAGE, 71),
                    new Student("1", Student.Course.MATHEMATICS, 82), new Student("2", Student.Course.ENGLISH, 69),
                    new Student("2", Student.Course.LANGUAGE, 66), new Student("2", Student.Course.MATHEMATICS, 46),
                    new Student("3", Student.Course.ENGLISH, 78), new Student("3", Student.Course.LANGUAGE, 88),
                    new Student("3", Student.Course.MATHEMATICS, 100), new Student("4", Student.Course.ENGLISH, 68),
                    new Student("4", Student.Course.LANGUAGE, 84), new Student("4", Student.Course.MATHEMATICS, 90),
                    new Student("5", Student.Course.ENGLISH, 74), new Student("5", Student.Course.LANGUAGE, 59),
                    new Student("5", Student.Course.MATHEMATICS, 87));

自定义收集器

public class StudentCollector implements Collector<Student, List<Student>, List<Student>> {
    @Override
    public Supplier<List<Student>> supplier() {
        return ArrayList::new;
    }

    @Override
    public BiConsumer<Set<Student>, Student> accumulator() {
        return (set, student) -> {
            Predicate<Student> studentPredicate = s -> s.getId().equals(student.getId());
            boolean b = set.stream().noneMatch(studentPredicate);
            if (b) {
                student.setCourse(Student.Course.TOTAL);
                set.add(student);
            }
            set.stream().filter(studentPredicate).forEach(s -> s.setScore(s.getScore() + student.getScore()));
        };
    }

    @Override
    public BinaryOperator<List<Student>> combiner() {
        return null;
    }

    @Override
    public Function<List<Student>, List<Student>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return EnumSet.of(Characteristics.IDENTITY_FINISH);
    }
}

调用

List<Student> studentList = students.stream().collect(new StudentCollector());
        System.out.println(studentList);

结果

需求2

将输出结果转化成TreeMap<Double, Student>,key为学生总分,value为学生自己,并按照总分从高到低排序。函数

数据结构与学生列表不变。优化

自定义收集器

public class StudentDiffCollectors implements Collector<Student, Set<Student>, TreeMap<Double, Student>> {
    @Override
    public Supplier<Set<Student>> supplier() {
        return HashSet::new;
    }

    @Override
    public BiConsumer<Set<Student>, Student> accumulator() {
        return (set, student) -> {
            Predicate<Student> studentPredicate = s -> s.getId().equals(student.getId());
            boolean b = set.stream().noneMatch(studentPredicate);
            if (b) {
                student.setCourse(Student.Course.TOTAL);
                set.add(student);
            }
            set.stream().filter(studentPredicate).forEach(s -> s.setScore(s.getScore() + student.getScore()));
        };
    }


    @Override
    public BinaryOperator<Set<Student>> combiner() {
        return null;
    }

    @Override
    public Function<Set<Student>, TreeMap<Double, Student>> finisher() {
        return list -> {
            TreeMap<Double, Student> doubleStudentTreeMap = new TreeMap<>(Comparator.reverseOrder());
            list.forEach(student -> doubleStudentTreeMap.put(student.getScore(), student));
            return doubleStudentTreeMap;
        };
    }

    @Override
    public Set<Characteristics> characteristics() {
        return EnumSet.of(Characteristics.CONCURRENT, Characteristics.UNORDERED);
    }
}

调用

TreeMap<Double, Student> collect = students.stream().collect(new StudentDiffCollectors());
        System.out.println(collect);

结果

进阶

除了实现接口之外,jdk还帮咱们提供了更简单的实现,只须要调用Collector.of()方法便可,以下所示:this

Collector<Student, List<Student>, List<Student>> studentCollector = Collector.of(ArrayList::new, (l, r) -> {
            // 计算逻辑
        }, (l, r) -> null, Function.identity(), Collector.Characteristics.IDENTITY_FINISH);

因为它符合A强转为R的条件,所以,能够更简化为:线程

Collector<Student, List<Student>, List<Student>> studentCollector = Collector.of(ArrayList::new, (l, r) -> {
            // 计算逻辑
        }, (l, r) -> null);

它的效果和实现接口Collector<Student, List<Student>, List<Student>>是同样的,但看起来更简洁明了!翻译

相关文章
相关标签/搜索