2020了你還不會Java8新特性?(五)收集器比較器用法詳解及原始碼剖析
收集器用法詳解與多級分組和分割槽
為什麼在collectors類中定義一個靜態內部類?
static class CollectorImpl<T, A, R> implements Collector<T, A, R>
設計上,本身就是一個輔助類,是一個工廠。作用是給開發者提供常見的收集器實現。提供的方法都是靜態方法,可以直接呼叫。
函數語言程式設計最大的特點:表示做什麼,而不是如何做。開發者更注重如做什麼,底層實現如何做。
/** * Implementations of {@link Collector} that implement various useful reduction * operations, such as accumulating elements into collections, summarizing * elements according to various criteria, etc. 沒有實現的方法,可以自己去編寫收集器。 * <p>The following are examples of using the predefined collectors to perform * common mutable reduction tasks: * 舉例: * <pre>{@code * // Accumulate names into a List 名字加入到一個集合。 * List<String> list = people.stream().map(Person::getName).collect(Collectors.toList()); * * // Accumulate names into a TreeSet 名字加入到一個Set。 待排序的集合。 * Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new)); * * // Convert elements to strings and concatenate them, separated by commas * String joined = things.stream() * .map(Object::toString) * .collect(Collectors.joining(", ")); * * // Compute sum of salaries of employee 計算員工工資的總數。 * int total = employees.stream() * .collect(Collectors.summingInt(Employee::getSalary))); * * // Group employees by department 對員工進行分組。 * Map<Department, List<Employee>> byDept * = employees.stream() * .collect(Collectors.groupingBy(Employee::getDepartment)); * * // Compute sum of salaries by department 根據部門計算工資的總數。 * Map<Department, Integer> totalByDept * = employees.stream() * .collect(Collectors.groupingBy(Employee::getDepartment, * Collectors.summingInt(Employee::getSalary))); * * // Partition students into passing and failing 將學生進行分割槽。 * Map<Boolean, List<Student>> passingFailing = * students.stream() * .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD)); * * }</pre> * * @since 1.8 提供了常見的方法。沒有的話可以去自定義。 */ public final class Collectors {
舉例。collector中的方法應用:
public static void main(String[] args) { Student student1 = new Student("zhangsan", 80); Student student2 = new Student("lisi", 90); Student student3 = new Student("wangwu", 100); Student student4 = new Student("zhaoliu", 90); Student student5 = new Student("zhaoliu", 90); List<Student> students = Arrays.asList(student1, student2, student3, student4, student5); //list 轉換成一個流,再轉換成一個集合. List<Student> students1 = students.stream().collect(Collectors.toList()); students1.forEach(System.out::println); System.out.println("- - - - - - -"); // collect 方法底層原理介紹. //有多種方法可以實現同一個功能.什麼方式更好呢? 越具體的方法越好. 減少自動裝箱拆箱操作. System.out.println("count:" + students.stream().collect(Collectors.counting())); System.out.println("count:" + (Long) students.stream().count()); System.out.println("- - - - - - - -"); //舉例練習 // 找出集合中分數最低的學生,打印出來. students.stream().collect(minBy(Comparator.comparingInt(Student::getScore))).ifPresent(System.out::println); // 找出集合中分數最大成績 students.stream().collect(maxBy(Comparator.comparingInt(Student::getScore))).ifPresent(System.out::println); // 求平均值 System.out.println(students.stream().collect(averagingInt(Student::getScore))); // 求分數的綜合 System.out.println(students.stream().collect(summingInt(Student::getScore))); // 求各種彙總資訊 結果為IntSummaryStatistics{count=5, sum=450, min=80, average=90.000000, max=100} System.out.println(students.stream().collect(summarizingInt(Student::getScore))); System.out.println(" - - - - - "); // 字串的拼接 結果為:zhangsanlisiwangwuzhaoliuzhaoliu System.out.println(students.stream().map(Student::getName).collect(joining())); //拼接加分隔符 結果為:zhangsan,lisi,wangwu,zhaoliu,zhaoliu System.out.println(students.stream().map(Student::getName).collect(joining(","))); // 拼接加前後綴 結果為:hello zhangsan,lisi,wangwu,zhaoliu,zhaoliu world System.out.println(students.stream().map(Student::getName).collect(joining(",", "hello ", " world"))); System.out.println("- - - - - - "); // group by 多層分組 // 根據分數和名字進行分組 輸出結果為: // {80={zhangsan=[Student{name='zhangsan', score=80}]}, // 100={wangwu=[Student{name='wangwu', score=100}]}, // 90={lisi=[Student{name='lisi', score=90}], zhaoliu=[Student{name='zhaoliu', score=90}, Student{name='zhaoliu', score=90}]}} Map<Integer, Map<String, List<Student>>> collect = students.stream().collect(groupingBy(Student::getScore, groupingBy(Student::getName))); System.out.println(collect); System.out.println("- - - - - - - "); // partitioningBy 多級分割槽 輸出結果為:{false=[Student{name='zhangsan', score=80}], true=[Student{name='lisi', score=90}, Student{name='wangwu', score=100}, Student{name='zhaoliu', score=90}, Student{name='zhaoliu', score=90}]} Map<Boolean, List<Student>> collect1 = students.stream().collect(partitioningBy(student -> student.getScore() > 80)); System.out.println(collect1); // 按照大於80分割槽,再按照90分割槽 //輸出結果為:{false={false=[Student{name='zhangsan', score=80}], true=[]}, true={false=[Student{name='lisi', score=90}, Student{name='zhaoliu', score=90}, Student{name='zhaoliu', score=90}], true=[Student{name='wangwu', score=100}]}} Map<Boolean, Map<Boolean, List<Student>>> collect2 = students.stream().collect(partitioningBy(student -> student.getScore() > 80, partitioningBy(student -> student.getScore() > 90))); System.out.println(collect2); //分割槽, 然後求出每個分組中的個數. 結果為:{false=1, true=4} Map<Boolean, Long> collect3 = students.stream().collect(partitioningBy(student -> student.getScore() > 80, counting())); System.out.println(collect3); System.out.println("- - - - - - - "); //根據名字分組,得到學生的分數 --, 使用collectingAndThen 求最小值,然後整合起來. 最後Optional.get()一定有值. students.stream().collect(groupingBy(Student::getName,collectingAndThen(minBy(Comparator.comparingInt(Student::getScore)), Optional::get))); }
Comparator比較器詳解與型別推斷特例
Comparator 比較器。引用了多個default方法。
完成一個功能時有多個方法,使用特化的方法。因為效率會更高。減少了裝箱拆箱的操作。減少效能損耗。
舉例: 簡單功能實現
public static void main(String[] args) { List<String> list = Arrays.asList("nihao", "hello", "world", "welcome"); //對list按照字母的升序排序 // list.stream().sorted().forEach(System.out::println); //按照字串的長度排序 // Collections.sort(list, (item1, item2) -> item1.length() - item2.length()); // Collections.sort(list, Comparator.comparingInt(String::length)); //字串的降序排序 // list.sort(Comparator.comparingInt(String::length).reversed()); // 下邊的形式會報錯 item識別成了(Obejct). //lambda表示式的型別推斷. 如果無法推斷型別,需要自己制定型別 // list.sort(Comparator.comparingInt(item-> item.length()).reversed()); //這樣寫就成功了. list.sort(Comparator.comparingInt((String item )-> item.length()).reversed()); //為什麼這個地方無法推斷型別? // 能推斷出的 : list.stream().... Strean<T> 傳遞的有引數. 精確的型別可以進行型別推斷. //這個地方沒有明確具體是什麼型別.ToIntFunction<? super T> .可以是String 或者在往上的父類 這個地方看成了Object類了. // list.sort(Comparator.comparingInt((Boolean item)-> 1).reversed()); //這種Boolean 就會報錯.編譯不通過. System.out.println(list); }
比較器深入舉例練習
舉例:兩層的比較.先按照字串的長度升序排序. 長度相同,根據每一個ASCII碼的順序排序、
thenComparing()多級排序的練習。;
List<String> list = Arrays.asList("nihao", "hello", "world", "welcome");
//兩層的比較.先按照字串的長度升序排序. 長度相同,根據每一個ASCII碼的升序排序. (不區分大小寫的 ,按照字母排序的規則) 幾種實現的方法。
list.sort(Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));
list.sort(Comparator.comparingInt(String::length).thenComparing((item1,item2) -> item1.toUpperCase().compareTo(item2.toUpperCase())));
list.sort(Comparator.comparingInt(String::length).thenComparing(Comparator.comparing(String::toUpperCase)));
//排序後將順序翻轉過來. reverseOrder();
list.sort(Comparator.comparingInt(String::length).thenComparing(String::toLowerCase,Comparator.reverseOrder()));
// 按照字串的長度降序排序, 再根據ASCII的降序排序
list.sort(Comparator.comparingInt(String::length).reversed()
.thenComparing(String::toLowerCase,Comparator.reverseOrder()));
//多級排序
list.sort(Comparator.comparingInt(String::length).reversed()
.thenComparing(String::toLowerCase, Comparator.reverseOrder())
.thenComparing(Comparator.reverseOrder()));
// 最後一個thenComparing()沒有發生作用。
自定義一個簡單的收集器
jdk提供了Collector介面。
public class MySetCollector<T> implements Collector<T,Set<T>,Set<T>> {
@Override
public Supplier<Set<T>> supplier() {
//用於提供一個空的容器
System.out.println("supplier invoked! ");
return HashSet::new; // 不接受物件,返回一個Set物件
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
// 累加器型別. 接收兩個引數不返回值.
//完成的功能: 不斷的往set中新增元素
System.out.println("accumulator invoked! ");
return Set<T>::add ;
// return HashSet<T>::add ; 返回HashSet報錯. 原因: 返回的是中間型別的返回型別. 不論返回什麼型別的Set ,Set都符合要求.
}
@Override
public BinaryOperator<Set<T>> combiner() {
//將並行流的多個結果給合併起來.
System.out.println("combiner invoked! ");
return (set1,set2)->{
set1.addAll(set2);
return set1;
};
}
@Override
public Function<Set<T>, Set<T>> finisher() {
//完成器,把所有的結果都合併在一起. 返回一個最終的結果型別
//如果中間型別 和最終結果型別一致, 不執行此方法;
System.out.println("finisher invoked! ");
// return t -> t ;
return Function.identity(); // 總是返回引數.
}
@Override
public Set<Characteristics> characteristics() {
System.out.println("characterstics invoked! ");
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH,Characteristics.UNORDERED)); // 這個地方 不給引數,IDENTITY_FINISH . 則會呼叫finisher()
}
public static void main(String[] args) {
List<String> list = Arrays.asList("hello", "world");
Set<String> collect = list.stream().collect(new MySetCollector<>());
System.out.println(collect);
}
輸出結果為:
supplier invoked!
accumulator invoked!
combiner invoked!
characterstics invoked!
characterstics invoked!
[world, hello]
}
接下來跟原始碼,看一下程式的呼叫過程。
@Override
@SuppressWarnings("unchecked")
public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
A container;
if (isParallel()
&& (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
&& (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
container = collector.supplier().get();
BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
forEach(u -> accumulator.accept(container, u));
}
else {
container = evaluate(ReduceOps.makeRef(collector));
}
return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
? (R) container
: collector.finisher().apply(container);
}
自定義收集器的深度剖析與並行缺陷
// 舉例: 需求:將一個Set,進行一個收集.對結果進行增強,封裝在一個map當中. // 輸入:Set<String> // 輸出:Map<String,String> // 示例輸入: [hello,world,hello world] // 示例輸出: {[hello,hello],[world,world],[hello world,hello world]}
public class MySetCollector2<T> implements Collector<T, Set<T>, Map<T, T>> {
@Override
public Supplier<Set<T>> supplier() {
System.out.println("supplier invoked!");
return HashSet::new;
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
System.out.println("accumlator invoked!");
return (set, item) -> {
set.add(item);
//每次呼叫 打印出執行緒 這裡會列印6次,
System.out.println("accunlator : " +set+ ", "+ Thread.currentThread().getName());
//出現異常的原因在這裡:
// 一個執行緒去修改一個集合,同時另外一個執行緒去迭代它(遍歷它)。程式就會丟擲併發修改異常。如果是並行操作的話,就不要在操作中額外的新增操作。新增就新增,別再去列印他。
};
}
@Override
public BinaryOperator<Set<T>> combiner() {
System.out.println("combiner invoked!");
//並行流的時候才會被呼叫. 將並行流的多個結果給合併起來
return (set1, set2) -> {
set1.addAll(set2);
return set2;
};
}
@Override
public Function<Set<T>, Map<T, T>> finisher() {
System.out.println("finisher invoked!");
// 中間型別和最終型別 一樣,這個是不會被呼叫的.
//這裡不一樣 . 會進行呼叫
return set -> {
Map<T, T> map = new HashMap<>();
// Map<T, T> map = new TreeMap<>(); 直接返回一個排序的Map
set.forEach(item -> map.put(item,item));
return map;
};
}
@Override
public Set<Characteristics> characteristics() {
System.out.println(" characteristics invoked");
return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED));// 這個引數不能亂寫. 要理解每個列舉的具體意思.
// return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED,Characteristics.CONCURRENT));// 這個引數不能亂寫. 要理解每個列舉的具體意思.
//加了這個引數 Characteristics.CONCURRENT
// 會出異常, 會正常執行. Caused by: java.util.ConcurrentModificationException
// return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED,Characteristics.IDENTITY_FINISH));
// 加了引數Characteristics.IDENTITY_FINISH . 會報錯
// Process 'command '/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1
// IDENTITY_FINISH 實際的含義: 如果用和這個引數,表示 Finish函式就是 identity函式。 並且轉換一定要是成功的。失敗的話會拋異常.
// 這個收集器具有什麼特性 ,由Characteristics 來定義. 就算你賦值的不實際,他也照樣執行.
}
public static void main(String[] args) {
List<String> list = Arrays.asList("hello","hello", "world", "helloworld","1","4","j");
Set<String> set = new HashSet<>(list);
System.out.println("set"+set);
// Map<String, String> collect = set.stream().collect(new MySetCollector2<>());
Map<String, String> collect = set.parallelStream().collect(new MySetCollector2<>()); //並行流
System.out.println(collect);
}
}
並行流缺陷詳解
並行:
accumlator invoked!
accunlator : [j], main
accunlator : [j, hello], main
accunlator : [helloworld, 4, j, hello], ForkJoinPool.commonPool-worker-2
accunlator : [helloworld, 1, 4, j, hello], ForkJoinPool.commonPool-worker-2
accunlator : [helloworld, 1, world, 4, j, hello], ForkJoinPool.commonPool-worker-2
序列。
accunlator : [j], main
accunlator : [helloworld], ForkJoinPool.commonPool-worker-11
accunlator : [helloworld, 1], ForkJoinPool.commonPool-worker-11
accunlator : [helloworld, 1, world], ForkJoinPool.commonPool-worker-11
accunlator : [4], ForkJoinPool.commonPool-worker-9
accunlator : [j, hello], main
/**
* Characteristics indicating properties of a {@code Collector}, which can
* be used to optimize reduction implementations.
*/
enum Characteristics { // 特徵
/**
* Indicates that this collector is <em>concurrent</em>, meaning that
* the result container can support the accumulator function being
* called concurrently with the same result container from multiple
* threads.
* 併發的,同一個結果容器可以由多個執行緒同時呼叫。
* <p>If a {@code CONCURRENT} collector is not also {@code UNORDERED},
* then it should only be evaluated concurrently if applied to an
* unordered data source.
如果不是UNORDERED。只能用於無序的資料來源。
如果不加CONCURRENT,還是可以操作並行流。但是操作的不是一個結果容器,而是多個結果容器。則需要呼叫finisher.
如果加了CONCURRENT,則是多個執行緒操作同一結果容器。 則無需呼叫finisher.
*/
CONCURRENT,
/**
* Indicates that the collection operation does not commit to preserving
* the encounter order of input elements. (This might be true if the
* result container has no intrinsic order, such as a {@link Set}.)
收集操作並不保留順序。無序的。
*/
UNORDERED,
/**
* Indicates that the finisher function is the identity function and
* can be elided. If set, it must be the case that an unchecked cast
* from A to R will succeed.
如果用和這個引數,表示 Finish函式就是 identity函式。 並且轉換一定要是成功的。不會呼叫Finish方法
*/
IDENTITY_FINISH
}
出異常的根本原因:
一個執行緒去修改一個集合,同時另外一個執行緒去迭代它(遍歷它)。程式就會丟擲併發修改異常。
如果是並行操作的話,就不要在操作中額外的新增操作。新增就新增,別再去列印他。
如果不加CONCURRENT,還是可以操作並行流。但是操作的不是一個結果容器,而是多個結果容器。則需要呼叫finisher.
如果加了CONCURRENT,則是多個執行緒操作同一結果容器。 則無需呼叫finisher.
超執行緒介紹:
超執行緒(HT, Hyper-Threading)是英特爾研發的一種技術,於2002年釋出。超執行緒技術原先只應用於Xeon 處理器中,當時稱為“Super-Threading”。之後陸續應用在Pentium 4 HT中。早期代號為Jackson。 [1]
通過此技術,英特爾實現在一個實體CPU中,提供兩個邏輯執行緒。之後的Pentium D縱使不支援超執行緒技術,但就集成了兩個實體核心,所以仍會見到兩個執行緒。超執行緒的未來發展,是提升處理器的邏輯執行緒。英特爾於2016年釋出的Core i7-6950X便是將10核心的處理器,加上超執行緒技術,使之成為20個邏輯執行緒的產品
收集器總結:
Collectors類中方法的實現練習。收集器總是有中間的容器。有必要的總結一下收集器中的方法。
當你具備一些前提的東西之後,你再去看難的東西就會覺得理所當然的。
對於Collectors靜態工廠類來說,實現一共分為兩種情況:
通過CollectorImpl來實現。
通過reducing方法來實現;reducing方法本身又是通過CollectorImpl實現的。
總的來說,都是通過CollectorImpl來實現的。
1. toCollection(collectionFactory) 。 將集合轉成指定的集合。
public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
return new CollectorImpl<>(collectionFactory, Collection<T>::add,
(r1, r2) -> { r1.addAll(r2); return r1; },
CH_ID);
}
2. toList()是 toCollection()方法的一種具體實現。
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
3. toSet() 是toCollection()方法的一種具體實現。
public static <T>
Collector<T, ?, Set<T>> toSet() {
return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
(left, right) -> { left.addAll(right); return left; },
CH_UNORDERED_ID);
}
4. joining(); 融合成一個字串。還有兩個過載的,單引數的和多引數的
public static Collector<CharSequence, ?, String> joining() {
return new CollectorImpl<CharSequence, StringBuilder, String>(
StringBuilder::new, StringBuilder::append,
(r1, r2) -> { r1.append(r2); return r1; },
StringBuilder::toString, CH_NOID);
}
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) {
return joining(delimiter, "", "");
}
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
return new CollectorImpl<>(
() -> new StringJoiner(delimiter, prefix, suffix),
StringJoiner::add, StringJoiner::merge,
StringJoiner::toString, CH_NOID);
}
5.mapping(); 將收集器的A 對映成B
public static <T, U, A, R>
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
Collector<? super U, A, R> downstream) {
BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
return new CollectorImpl<>(downstream.supplier(),
(r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)),
downstream.combiner(), downstream.finisher(),
downstream.characteristics());
}
such as :
Map<City, Set<String>> lastNamesByCity
= people.stream().collect(groupingBy(Person::getCity, mapping(Person::getLastName, toSet())));
6.collectingAndThen(); 收集處理轉換完後, 再去進行一個轉換。
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher) {
Set<Collector.Characteristics> characteristics = downstream.characteristics();
if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) {
if (characteristics.size() == 1)
characteristics = Collectors.CH_NOID;
else {
characteristics = EnumSet.copyOf(characteristics);
characteristics.remove(Collector.Characteristics.IDENTITY_FINISH);
// 這個地方為什麼要把IDENTITY_FINISH 去掉。
// 如果不去掉的話, 最終結果直接返回中間結果的型別
characteristics = Collections.unmodifiableSet(characteristics);
}
}
return new CollectorImpl<>(downstream.supplier(),
downstream.accumulator(),
downstream.combiner(),
downstream.finisher().andThen(finisher),
characteristics);
}
such as :
List<String> people
= people.stream().collect(collectingAndThen(toList(),Collections::unmodifiableList));
7. counting(); 計數。
public static <T> Collector<T, ?, Long>
counting() {
return reducing(0L, e -> 1L, Long::sum);
}
8. 最大值最小值
public static <T> Collector<T, ?, Optional<T>>
minBy(Comparator<? super T> comparator) {
return reducing(BinaryOperator.minBy(comparator));
}
public static <T> Collector<T, ?, Optional<T>>
maxBy(Comparator<? super T> comparator) {
return reducing(BinaryOperator.maxBy(comparator));
}
9. summingInt();求和。
public static <T> Collector<T, ?, Integer>
summingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new int[1], // 這個地方為什麼不可以用一個0,來當做中間型別呢?數字本身是一個值型別的,不可變的,沒法引用。陣列本身是一個引用型別,可以進行傳遞。陣列本身是一個容器。
(a, t) -> { a[0] += mapper.applyAsInt(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
public static <T> Collector<T, ?, Long>
summingLong(ToLongFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new long[1],
(a, t) -> { a[0] += mapper.applyAsLong(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
public static <T> Collector<T, ?, Double>
summingDouble(ToDoubleFunction<? super T> mapper) {
/*
* In the arrays allocated for the collect operation, index 0
* holds the high-order bits of the running sum, index 1 holds
* the low-order bits of the sum computed via compensated
* summation, and index 2 holds the simple sum used to compute
* the proper result if the stream contains infinite values of
* the same sign.
*/
return new CollectorImpl<>(
() -> new double[3],
(a, t) -> { sumWithCompensation(a, mapper.applyAsDouble(t));
a[2] += mapper.applyAsDouble(t);},
(a, b) -> { sumWithCompensation(a, b[0]);
a[2] += b[2];
return sumWithCompensation(a, b[1]); },
a -> computeFinalSum(a),
CH_NOID);
}
10. 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);
}
public static <T> Collector<T, ?, Double>
averagingLong(ToLongFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new long[2],
(a, t) -> { a[0] += mapper.applyAsLong(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);
}
public static <T> Collector<T, ?, Double>
averagingDouble(ToDoubleFunction<? super T> mapper) {
/*
* In the arrays allocated for the collect operation, index 0
* holds the high-order bits of the running sum, index 1 holds
* the low-order bits of the sum computed via compensated
* summation, and index 2 holds the number of values seen.
*/
return new CollectorImpl<>(
() -> new double[4],
(a, t) -> { sumWithCompensation(a, mapper.applyAsDouble(t)); a[2]++; a[3]+= mapper.applyAsDouble(t);},
(a, b) -> { sumWithCompensation(a, b[0]); sumWithCompensation(a, b[1]); a[2] += b[2]; a[3] += b[3]; return a; },
a -> (a[2] == 0) ? 0.0d : (computeFinalSum(a) / a[2]),
CH_NOID);
}
11. reducing() ; 詳解。
public static <T> Collector<T, ?, T>
reducing(T identity, BinaryOperator<T> op) {
return new CollectorImpl<>(
boxSupplier(identity),
(a, t) -> { a[0] = op.apply(a[0], t); },
(a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
a -> a[0],
CH_NOID);
}
12. groupingBy(); 分組方法詳解。
public static <T, K> Collector<T, ?, Map<K, List<T>>> //使用者本身不注重中間型別怎麼操作。
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList()); //呼叫兩個引數的 groupingBy();
}
* @param <T> the type of the input elements //T; 接收的型別。
* @param <K> the type of the keys // K,分類器函式中間返回結果的型別。
* @param <A> the intermediate accumulation type of the downstream collector
* @param <D> the result type of the downstream reduction
*
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream); // 呼叫三引數的 groupingBy()
}
//功能最完全的groupingBy();
/**
* Returns a {@code Collector} implementing a cascaded "group by" operation
* on input elements of type {@code T}, grouping elements according to a
* classification function, and then performing a reduction operation on
* the values associated with a given key using the specified downstream
* {@code Collector}. The {@code Map} produced by the Collector is created
* with the supplied factory function.
*
* <p>The classification function maps elements to some key type {@code K}.
* The downstream collector operates on elements of type {@code T} and
* produces a result of type {@code D}. The resulting collector produces a
* {@code Map<K, D>}.
*
* <p>For example, to compute the set of last names of people in each city,
* where the city names are sorted:
* <pre>{@code
* Map<City, Set<String>> namesByCity
* = people.stream().collect(groupingBy(Person::getCity, TreeMap::new,
* mapping(Person::getLastName, toSet())));
* }</pre>
*
* @implNote
* The returned {@code Collector} is not concurrent. For parallel stream
* pipelines, the {@code combiner} function operates by merging the keys
* from one map into another, which can be an expensive operation. If
* preservation of the order in which elements are presented to the downstream
* collector is not required, using {@link #groupingByConcurrent(Function, Supplier, Collector)}
* may offer better parallel performance.
* 返回的 並不是併發的。如果順序並不是很重要的話, 推薦使用groupingByConcurrent(); 併發的分組函式。
* @param <T> the type of the input elements
* @param <K> the type of the keys
* @param <A> the intermediate accumulation type of the downstream collector
* @param <D> the result type of the downstream reduction
* @param <M> the type of the resulting {@code Map}
* @param classifier a classifier function mapping input elements to keys
* @param downstream a {@code Collector} implementing the downstream reduction
* @param mapFactory a function which, when called, produces a new empty
* {@code Map} of the desired type
* @return a {@code Collector} implementing the cascaded group-by operation
*
* @see #groupingBy(Function, Collector)
* @see #groupingBy(Function)
* @see #groupingByConcurrent(Function, Supplier, Collector)
*/
public static <T, K, D, A, M extends Map<K, D>>
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier();
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(container, t);
};
BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner()); //接收兩個引數,參會一個結果。
@SuppressWarnings("unchecked")
Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory; // 進行一個強制的型別轉換。
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
//如果 IDENTITY_FINISH , 則不用呼叫finisher方法。
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
}
else {
@SuppressWarnings("unchecked")
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
Function<Map<K, A>, M> finisher = intermediate -> {
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
}
}
13. groupingByConcurrent(); 併發的分組方法。 使用前提是對資料裡邊的順序沒有要求。
/**
* Returns a concurrent {@code Collector} implementing a cascaded "group by"
* operation on input elements of type {@code T}, grouping elements
* according to a classification function, and then performing a reduction
* operation on the values associated with a given key using the specified
* downstream {@code Collector}.
*/ // ConcurrentHashMap 是一個支援併發的Map
public static <T, K>
Collector<T, ?, ConcurrentMap<K, List<T>>>
groupingByConcurrent(Function<? super T, ? extends K> classifier) {
return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList());
}
public static <T, K, A, D>
Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream) {
return groupingByConcurrent(classifier, ConcurrentHashMap::new, downstream);
}
public static <T, K, A, D, M extends ConcurrentMap<K, D>>
Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier();
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BinaryOperator<ConcurrentMap<K, A>> merger = Collectors.<K, A, ConcurrentMap<K, A>>mapMerger(downstream.combiner());
@SuppressWarnings("unchecked")
Supplier<ConcurrentMap<K, A>> mangledFactory = (Supplier<ConcurrentMap<K, A>>) mapFactory;
BiConsumer<ConcurrentMap<K, A>, T> accumulator;
if (downstream.characteristics().contains(Collector.Characteristics.CONCURRENT)) {
accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(resultContainer, t);
};
}
else {
accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get());
synchronized (resultContainer) { // 這裡有一個同步的操作。雖然是多執行緒操作同一容器,但是同時還是隻有一個執行緒操作,進行了同步。
downstreamAccumulator.accept(resultContainer, t);
}
};
}
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_CONCURRENT_ID);
}
else {
@SuppressWarnings("unchecked")
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
Function<ConcurrentMap<K, A>, M> finisher = intermediate -> {
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_CONCURRENT_NOID);
}
}
14. partitioningBy(); 分割槽詳解。
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
public static <T, D, A>
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream) {
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BiConsumer<Partition<A>, T> accumulator = (result, t) ->
downstreamAccumulator.accept(predicate.test(t) ? result.forTrue : result.forFalse, t);
BinaryOperator<A> op = downstream.combiner();
BinaryOperator<Partition<A>> merger = (left, right) ->
new Partition<>(op.apply(left.forTrue, right.forTrue),
op.apply(left.forFalse, right.forFalse));
Supplier<Partition<A>> supplier = () ->
new Partition<>(downstream.supplier().get(),
downstream.supplier().get());
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(supplier, accumulator, merger, CH_ID);
}
else {
Function<Partition<A>, Map<Boolean, D>> finisher = par ->
new Partition<>(downstream.finisher().apply(par.forTrue),
downstream.finisher().apply(par.forFalse));
return new CollectorImpl<>(supplier, accumulator, merger, finisher, CH_NOID);
}
}
jdk的程式碼,就是我們學習的範本。
講這麼細的原因並不是因為要自己去寫,是為了瞭解內部是具體怎麼實現的。呼叫的時候就信心非常的足。
附一個小插曲。
相關推薦
2020了你還不會Java8新特性?方法引用詳解及Stream 流介紹和操作方式詳解(三)
方法引用詳解 方法引用: method reference 方法引用實際上是Lambda表示式的一種語法糖 我們可以將方法引用看作是一個「函式指標」,function pointer 方法引用共分為4類: 類名::靜態方法名 引用名(物件名)::例項方法名 類名::例項方法名 (比較不好理解,個地方呼叫的方
2020了你還不會Java8新特性?(五)收集器比較器用法詳解及原始碼剖析
收集器用法詳解與多級分組和分割槽 為什麼在collectors類中定義一個靜態內部類? static class CollectorImpl<T, A, R> implements Collector<T, A, R> 設計上,本身就是一個輔助類,是一個工廠。作用是給開發者提供常見的
2020了你還不會Java8新特性?(六)Stream原始碼剖析
Stream流原始碼詳解 節前小插曲 AutoCloseable介面: 通過一個例子 舉例自動關閉流的實現。 public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable
2020你還不會Java8新特性?(學習過程記錄)
Java8(1)新特性介紹及Lambda表示式 前言: 跟大娃一塊看,把原來的電腦拿出來放中間看視訊用 --- 以後會有的課程 難度 深入Java 8 難度1 併發與netty 難度3 JVM 難度4 node 難度2 spring精髓 難度1 課程中提到的知識: 前後端分離的開發,是靠node當做中間的
Java8新特性(五)新日期時間的API
1、新時間日期API java.time – 包含值物件的基礎包 java.time.chrono – 提供對不同的日曆系統的訪問 java.time.format – 格式化和解析時間和日期 java.time.temporal – 包括底層框架和擴
Java8新特性(一)---Lambda表示式
Java8新特性之Lambda表示式 Lambda的語法格式 語法格式一:無引數,無返回值 語法格式二:有一個引數,並且無返回值 語法格式三:若只有一個引
Java8新特性(三)方法引用、構造器引用、陣列引用
目錄: 1、方法引用 1.1、物件 :: 例項方法 1.2、類 :: 靜態方法 1.3、類 ::例項方法 2、構造器引用 2.1、介紹 2.2、使用 3、陣列引用 3.1、介紹 3.2、使用 1、方法引用 若lambda體中的內容有方法已經實現了,
java8新特性(四):Stream流的使用
Java5的時候就引入了自動裝箱拆箱的功能, 在對包裝型別進行數學計算的時候,包裝型別就會被自動拆箱成基本型別, 而將一個基本型別的值賦值給一個包裝型別的變數或者放入集合中時基本型別又會被自動裝箱成包裝型別,這個過程是需要消耗計算效能的。Java8的包裝型別的流的計算過程中同樣包含了對基本型別的自動裝箱和拆箱
Java8新特性(二)方法引用
方法引用 方法引用其實就是Lambda表示式的簡寫,標誌是 :: 1、和Lambda表示式比較 public class Java8Test { public s
Java8新特性(四)------介面中可以定義方法體
Java8比起以前的版本存在很大的變化,我們知道在之前的版本中介面只能是定義抽象的方法,是不能定義實現的,但是在java8環境下,這個不可能已經變得可能。下面我們通過例子一步一步的來講解下java8
都2020年了,聽說你還不會歸併排序?手把手教你手寫歸併排序演算法
本文介紹了歸併排序的基本思想,遞迴方法的一般寫法,最後一步步手寫歸併排序,並對其效能進行了分析。 基本思想 歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法的一個非常典型的應用。即先使每個子序列有序,再將已有序的子序列合併,得到完全有序的序列。這裡給出一種遞迴形式的歸併排序實現。 遞迴方
都2020年了,你還不會寫簡潔的Java程式碼!
# 都2020年了,你還不會寫簡潔的Java程式碼! ## 使用Google Guava依賴 ```xml com.google.guava guava 29.0-jre ``` ## 建立一個list集合並賦值 最原始的辦法: ```java List stringList =
都9102年了,還不會Docker?10分鐘帶你從入門操作到實戰上手
Docker簡述 Docker是一種OS虛擬化技術,是一個開源的應用容器引擎。它可以讓開發者將應用打包到一個可移植的容器中,並且該容器可以執行在幾乎所有linux系統中(Windows10目前也原生支援,Win10前需要內建虛擬機器),正所謂“一次打包,到處執行”。 Docker容器的執行是完全的沙箱機制,相
你學Python多久了?為什麽你還不會做爬蟲?
希望 興趣 pass 回憶一下 經驗 imp 更多 提問 差距 學習是個很有意思的事,有的人隨便學學就能很快學會,而有的人明明很努力卻什麽都沒學會,這是為甚呢?有的人學了3個月,甚至更久卻連一個項目或者一個爬蟲都不會做,這究其原因是和你的學習效率有關。對於大家所問的Pyth
活動 | Siri都開放了,你還不會做“聲控”APP?
【攜程技術微分享】是由攜程技術中心推出的線上公開分享課程,每月1-2期,邀請攜程技術人,面向廣大程式設計師和技術愛好者,一起探討最新的技術熱點,分享一線實戰經驗。在6月中剛剛結束的2016蘋果全球開發者大會上, 人工智慧助手Siri又一次成為焦點。Siri In
CentOS 8 都發布了,你還不會用 nftables?
原文連結:CentOS 8 都發布了,你還不會用 nftables? 如果你沒有生活在上個世紀,並且是雲端計算或相關領域的一名搬磚者,那你應該聽說最近 CentOS 8 官方正式版已經發布了,CentOS 完全遵守 Red Hat 的再發行政策,並且致力與上游產品在功能上完全相容。CentOS 8 主要改
【Spring註解驅動開發】你還不會使用@Resource和@Inject註解?那你就out了!!
## 寫在前面 > 我在 **冰河技術** 微信公眾號中發表的《[【Spring註解驅動開發】使用@Autowired@Qualifier@Primary三大註解自動裝配元件,你會了嗎?](https://mp.weixin.qq.com/s?__biz=Mzg3MzE1NTIzNA==&mi
小竈時間-如果你還不會用Python虛擬環境
小竈時間 python環境 conda anaconda pip 一個鼓搗電腦多年的程序猿,帶給你的幾點編程套路和幾個靈巧工具,希望為你的編程之路添磚加瓦,加血回藍,一起拼荊斬棘,共同成長。統稱:小竈時間,作者:第8哥。 1. 為什麽用Python虛擬環境 實際工作中,我們接觸的 Pyt
看完這個你還不會 插入排序 麼
前言 由於LeetCode上的演算法題很多涉及到一些基礎的資料結構,為了更好的理解後續更新的一些複雜題目的動畫,推出一個新系列 -----《圖解資料結構》,主要使用動畫來描述常見的資料結構和演算法。本系列包括十大排序、堆、佇列、樹、並查集、圖等等大概幾十篇。 插入排序 插入排序的程式碼實現雖然沒有氣泡排
聽說你還不會用Dagger2?Dagger2 For Android最佳實踐教程
前言 Dagger2是現在非常火的一個依賴注入框架,目前由Google維護,在Github上面已經有12K star了。Dagger2的入門門檻其實是比較高的,據瞭解,目前有很多Android工程師對Dagger2還不甚瞭解,沒有用上Dagger2或者是用法有問題,本文的主旨就是讓Android工程