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>>是同樣的,但看起來更簡潔明瞭!翻譯

相關文章
相關標籤/搜索