JAVA8學習——深入Comparator&Collector(學習過程)
深入Comparator&Collector
從原始碼深入Comparator
Comparator從Java1.2就出來了,但是在1.8的時候,又添加了大量的預設方法.
compare() equals() reversed() //倒序 thenComparing(Comparator<? super T> other) //然後,再去比較. thenComparing( Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator) //先通過第一個比較器,再執行第二個比較器...串聯 thenComparing() thenComparingInt() thenComparingLong() thenComparingDouble() reverseOrder() naturalOrder() nullsFirst() nullsLast() comparing () //靜態方法 comparing() comparingInt() comparingLong() comparingDouble()
### 從Demo程式碼看Comparator
package com.dawa.jdk8.stream2; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; //關於比較器comparator,案例詳解. public class MyComparator { public static void main(String[] args) { List<String> list = Arrays.asList("hello", "world", "welcome", "nihao"); //按照字母排序 Collections.sort(list); System.out.println(list); //按照字串的長度. Collections.sort(list, (item1, item2) -> item1.length() - item2.length()); System.out.println(list); //按照字串的長度降序排序. Collections.sort(list, (item1, item2) -> item2.length() - item1.length()); //使用方法引用 //長度排序 Collections.sort(list, Comparator.comparingInt(String::length)); System.out.println(list); //長度倒敘排序 Collections.sort(list, Comparator.comparingInt(String::length).reversed()); System.out.println(list); //使用lambda表示式實現上述兩個方法 // Collections.sort(list,Comparator.comparingInt(item->item.length()).reversed()); //這裡,reversed()方法,引數要的是Object型別. //引數的型別推斷. Collections.sort(list,Comparator.comparingInt((String item)->item.length()).reversed()); //這樣寫就行了. //問題:之前為什麼會成功? 因為是從Stream<T> 型別開始推斷的,可以獲取到原屬性的元素. //問題:為什麼上述的型別推斷失敗了/? 看sort方法的 Comparator類的泛型<T>,T是傳入引數的泛型- <? super T>. // String上的型別.你沒指定,編譯器也沒辦法幫你指定. // public static <T> void sort(List<T> list, Comparator<? super T> c) { // list.sort(c); // } //如: Collections.sort(list,Comparator.comparingInt((Boolean item)->1).reversed()); //這樣不會被相容.因為Boolean 不是 String的上型別. //如: Collections.sort(list,Comparator.comparingInt((Object item)->1).reversed()); //這樣就是可以的. //如: Collections.sort(list,Comparator.comparingInt(item->item.length()); //這樣也是可以的. } }
@SuppressWarnings({"unchecked", "rawtypes"})
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
關於: <? super T> 泛型的使用.需要注意.
語義更寬泛,但是從實際結果型別,實際就是T型別本身.這個需要仔細思考一下.
Comparator比較器的串聯使用
//通過兩層比較,1:排序(升序) ,2:字母順序排序. 使用thenComparing() Collections.sort(list,Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));
thenComparing()方法原始碼如下
/**
* Returns a lexicographic-order comparator with another comparator.
* If this {@code Comparator} considers two elements equal, i.e.
* {@code compare(a, b) == 0}, {@code other} is used to determine the order.
*
* <p>The returned comparator is serializable if the specified comparator
* is also serializable.
*
* @apiNote
* For example, to sort a collection of {@code String} based on the length
* and then case-insensitive natural ordering, the comparator can be
* composed using following code,
*
不區分大小寫,的實現. 技術上述案例.
* <pre>{@code
* Comparator<String> cmp = Comparator.comparingInt(String::length)
* .thenComparing(String.CASE_INSENSITIVE_ORDER);
* }</pre>
*
* @param other the other comparator to be used when this comparator
* compares two objects that are equal.
* @return a lexicographic-order comparator composed of this and then the
* other comparator
* @throws NullPointerException if the argument is null.
* @since 1.8
*/
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
前面比較器的結果等於0,這個thenComparing()才會被呼叫. 就如三個長度相同的那三個數,才會被二次排序.
也就是說如果第一個比較器,能夠排序,就用第一個,第一個排序不成再用第二個.
另一種實現
Collections.
sort(list,Comparator.comparingInt(String::length).
thenComparing((item1,item2)->item1.toLowerCase().compareTo(item2)));
另一種實現
Collections.sort(list,Comparator.comparingInt(String::length).thenComparing(Comparator.comparing(String::toUpperCase)));
另一種實現
Collections.sort(list,Comparator.comparingInt(String::length).thenComparing(Comparator.comparing(String::toLowerCase,Comparator.reverseOrder())));
上述幾個案例,主要就是對於 thenComparing()方法的不同使用實現.
那麼,下面這個方法的輸出結果是什麼?
Collections.sort(list,Comparator.comparingInt(String::length).thenComparing(Comparator.comparing(String::toLowerCase,Comparator.reverseOrder())));
再次重複一下:前面比較器的結果等於0,這個thenComparing()才會被呼叫. 就如三個長度相同的那三個數,才會被二次排序.也就是說如果第一個比較器,能夠排序,就用第一個,第一個排序不成再用第二個.
多級排序
Collections.sort(list,Comparator.comparingInt(String::length).reversed()
.thenComparing(Comparator.comparing(String::toLowerCase, Comparator.reverseOrder()))
.thenComparing(Comparator.reverseOrder()));
JDK1.8之前,Collections裡面提供的方法是很少的,從JDK1.8之後,新增了大量的實現方法和具體的特化的實現.
避免了裝箱和拆箱操作.這也可能會影響效能.
自定義Collector實現類
實現Collector介面
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner,
Characteristics... characteristics) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(accumulator);
Objects.requireNonNull(combiner);
Objects.requireNonNull(characteristics);
Set<Characteristics> cs = (characteristics.length == 0)
? Collectors.CH_ID
: Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH,
characteristics));
return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, cs);
}
public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A, R> finisher,
Characteristics... characteristics) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(accumulator);
Objects.requireNonNull(combiner);
Objects.requireNonNull(finisher);
Objects.requireNonNull(characteristics);
Set<Characteristics> cs = Collectors.CH_NOID;
if (characteristics.length > 0) {
cs = EnumSet.noneOf(Characteristics.class);
Collections.addAll(cs, characteristics);
cs = Collections.unmodifiableSet(cs);
}
return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, finisher, cs);
}
Characteristics {
CONCURRENT,
UNORDERED,
IDENTITY_FINISH
}
}
自定義的收集器
package com.dawa.jdk8.stream2;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import static java.util.stream.Collector.Characteristics.IDENTITY_FINISH;
public class MySetCollector<T> implements Collector<T,Set<T>,Set<T>> {
@Override
public Supplier<Set<T>> supplier() {
System.out.println("supplier invoked");
return HashSet<T>::new;// 返回一個HasHSet容器.
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
System.out.println("accumalator invoked");//累加器
return Set<T>::add;
// return HashSet<T>::add; //不行,沒有靜態方法支援. 應該是 Supplier返回值的父類介面. 不能使用具體型別的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 ts -> ts;
return Function.identity(); //底層是一樣的. 同一性.
}
@Override
public Set<Characteristics> characteristics() {
System.out.println("charcteristics invoked ");
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
}
public static void main(String[] args) {
List<String> list = Arrays.asList("hello", "world", "welcome");
Set<String> collect = list.stream().collect(new MySetCollector<>());
System.out.println(collect);
}
}
從原始碼深入Collector
第一步:程式碼中呼叫collect()
public static void main(String[] args) {
List<String> list = Arrays.asList("hello", "world", "welcome");
Set<String> collect = list.stream().collect(new MySetCollector<>());
System.out.println(collect);
}
第二步:collect()方法的實現類
@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);
}
IDENTITY_FINISH的欄位特別重要,在這裡使用
第三步: makeRef(), 逐步呼叫者三個函式式介面物件
public static <T, I> TerminalOp<T, I>
makeRef(Collector<? super T, I, ?> collector) {
Supplier<I> supplier = Objects.requireNonNull(collector).supplier();
BiConsumer<I, ? super T> accumulator = collector.accumulator();
BinaryOperator<I> combiner = collector.combiner();
class ReducingSink extends Box<I>
implements AccumulatingSink<T, I, ReducingSink> {
@Override
public void begin(long size) {
state = supplier.get();
}
@Override
public void accept(T t) {
accumulator.accept(state, t);
}
@Override
public void combine(ReducingSink other) {
state = combiner.apply(state, other.state);
}
}
return new ReduceOp<T, I, ReducingSink>(StreamShape.REFERENCE) {
@Override
public ReducingSink makeSink() {
return new ReducingSink();
}
@Override
public int getOpFlags() {
return collector.characteristics().contains(Collector.Characteristics.UNORDERED)
? StreamOpFlag.NOT_ORDERED
: 0;
}
};
}
Collector的一些"坑"
使用這個案例去理解運作過程.
把一個set集合進行收集,我們對結果做一個增強.(原來是直接放在set當中了.)我們現在放在Map當中.
宣告一個Collector類,要求.
- 輸入:Set
- 輸出:Map<String,String>
示例輸入:["hello","world","hello world"]
示例輸出:[{hello,hello},{world,world},{hello world,hello world}
泛型:<T,T,T>
徹底理解Characteristics.IDENTITY_FINISH屬性
package com.dawa.jdk8.stream2;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
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<T>::new;
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
System.out.println("accumulator invoked");
return Set::add;
}
@Override
public BinaryOperator<Set<T>> combiner() {
System.out.println("combiner invoked");
return (set1, set2) -> {
set1.addAll(set2);
return set1;
};
}
@Override
public Function<Set<T>, Map<T, T>> finisher() { //這裡一定會被呼叫.因為結果型別和最終型別不同
//示例輸入:["hello","world","hello world"]
//示例輸出:[{hello,hello},{world,world},{hello world,hello world}
System.out.println("finisher invoked");
return set ->{
Map<T, T> map = new HashMap<>();
set.stream().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));
}
public static void main(String[] args) {
List<String> list = Arrays.asList("hello", "world", "hello", "welocome", "a", "b", "c", "d", "e");
HashSet<String> set = new HashSet<>(list);
System.out.println("set:"+list);
Map<String, String> collect = set.stream().collect(new MySetCollector2<>());
System.out.println(collect);
}
}
如果多一個引數:
return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED,Characteristics.IDENTITY_FINISH));
則會出現型別轉換異常.
/**
* 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.
*/
IDENTITY_FINISH
如果定義這個屬性,則代表 indentity和 finish 是同一個型別的,要執行強制型別轉換.所以會出現上述異常.
收集器是什麼特性的,都是由這個Characteristics類來由你定義的.
所以你必須要理解你寫的程式的型別.才能正確的使用這個列舉定義類.
徹底理解Characteristics.CONCURRENT屬性
分支合併框架ForkJoinPoll(並行流)
對程式進行一定的改造,打印出相應的執行緒名稱
@Override
public BiConsumer<Set<T>, T> accumulator() {
System.out.println("accumulator invoked");
return (set,item)->{
System.out.println("accumulator:"+ Thread.currentThread().getName());
set.add(item);
};
}
- 序列情況下:
Map<String, String> collect = set.Stream().collect(new MySetCollector2<>());
執行結果如下:
- 並行情況下
Map<String, String> collect = set.parallelStream().collect(new MySetCollector2<>());
執行結果如下.
如果加上 Characteristics.CONCURRENT.
@Override
public Set<Characteristics> characteristics() {
System.out.println("characteristics invoked");
return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED,Characteristics.CONCURRENT));
}
則可能會出來一個異常
Caused by: java.util.ConcurrentModificationException
如果不加 ,則不會出現異常
多執行幾次,會有一定的發現.
檢視屬性的原始碼.
/**
* 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.
*/
CONCURRENT,
出現問題的原因:是在列印了set集合.
/**
* This exception may be thrown by methods that have detected concurrent
* modification of an object when such modification is not permissible.
* <p>
* For example, it is not generally permissible for one thread to modify a Collection
* while another thread is iterating over it. In general, the results of the
* iteration are undefined under these circumstances. Some Iterator
* implementations (including those of all the general purpose collection implementations
* provided by the JRE) may choose to throw this exception if this behavior is
* detected. Iterators that do this are known as <i>fail-fast</i> iterators,
* as they fail quickly and cleanly, rather that risking arbitrary,
* non-deterministic behavior at an undetermined time in the future.
* <p>
* Note that this exception does not always indicate that an object has
* been concurrently modified by a <i>different</i> thread. If a single
* thread issues a sequence of method invocations that violates the
* contract of an object, the object may throw this exception. For
* example, if a thread modifies a collection directly while it is
* iterating over the collection with a fail-fast iterator, the iterator
* will throw this exception.
*
* <p>Note that fail-fast behavior cannot be guaranteed as it is, generally
* speaking, impossible to make any hard guarantees in the presence of
* unsynchronized concurrent modification. Fail-fast operations
* throw {@code ConcurrentModificationException} on a best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>{@code ConcurrentModificationException}
* should be used only to detect bugs.</i>
*
* @author Josh Bloch
* @see Collection
* @see Iterator
* @see Spliterator
* @see ListIterator
* @see Vector
* @see LinkedList
* @see HashSet
* @see Hashtable
* @see TreeMap
* @see AbstractList
* @since 1.2
*/
public class ConcurrentModificationException extends RuntimeException {
}
併發修改異常.
因為如果加上這個屬性,那麼這個就有一個結果集
並行的時候,會對set進行操作,但是你同時又在遍歷列印, 兩個趕到一起了.然後就會丟擲這個異常.
這就是丟擲這個異常的根本原因.
注意:如果是並行的話,千萬要避免 列印遍歷 你要操作的物件.
如果不加這個屬性,那麼combiner()方法的中間結果集就會被呼叫,所以就不會出現搶佔資源的現象.
擴充套件: sequential() && parallerl()方法的呼叫.
Set<String> collect = list.stream().parallel().sequential().sequential().parallel().collect(new MySetCollector<>());
只有最後一個會生效.
sequential()
/**
* Returns an equivalent stream that is sequential. May return
* itself, either because the stream was already sequential, or because
* the underlying stream state was modified to be sequential.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @return a sequential stream
*/
S sequential();
parallerl()
/**
* Returns an equivalent stream that is parallel. May return
* itself, either because the stream was already parallel, or because
* the underlying stream state was modified to be parallel.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @return a parallel stream
*/
S parallel();
關於Supplier()容器的定義.
修改程式碼.檢視 序列 和並行的 區別.
@Override
public Supplier<Set<T>> supplier() {
System.out.println("supplier invoked");
// return HashSet<T>::new;// 返回一個HasHSet容器.
System.out.println("-----");
return HashSet::new;
}
結論:序列的時候,會生成單個初始容器 / 並行的時候,會生成多個初始容器.
關於序列和並行的效率問題
並不是說序列的效率就一定比並行的效率低.這都是要看實際情況的.
最多會生成系統最大CPU核心
超執行緒技術
Collectors類方法詳解
題外話:當你具備一些底層基礎知識之後,你看一些東西會覺得是理所當然的.
如果你不具備這些知識的話,是看不懂的.雲裡霧裡的.
關注一下JDK提供的方法是怎麼實現的.對於Collectors靜態工廠類來說,其實現一共分為兩種方式.
- 通過CollectorImpl來實現
- 通過reducing來實現 (reducing本身又是通過CollectorImpl來實現)
所以,所有的方法都是通過CollectorImpl來實現的.
- 4個變數
static final Set<Collector.Characteristics> CH_CONCURRENT_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_CONCURRENT_NOID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED));
static final Set<Collector.Characteristics> CH_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_UNORDERED_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
- toCollection()方法
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);
}
- 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);
}
- 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);
}
- 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);
}
- mapping() 對映函式
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());
}
collectingAndThen() 收集,並且做處理
原理:把IDENTITY_FINISH識別符號給去掉.
為什麼要去掉:不去掉的話,表示不會執行 finisher()方法.
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);
characteristics = Collections.unmodifiableSet(characteristics);
}
}
return new CollectorImpl<>(downstream.supplier(),
downstream.accumulator(),
downstream.combiner(),
downstream.finisher().andThen(finisher),
characteristics);
}
- counting() 計算.
public static <T> Collector<T, ?, Long>
counting() {
return reducing(0L, e -> 1L, Long::sum);
}
- minBy()
public static <T> Collector<T, ?, Optional<T>>
minBy(Comparator<? super T> comparator) {
return reducing(BinaryOperator.minBy(comparator));
}
- maxBy()
public static <T> Collector<T, ?, Optional<T>>
maxBy(Comparator<? super T> comparator) {
return reducing(BinaryOperator.maxBy(comparator));
}
summingInt(),Long(),Double
為什麼要用一個 int[1]? 最後還要返回一個數組中的單個數組呢?直接用一個數組行不行.
因為:不行,因為直接用數字,數字是不能被傳遞的. 陣列本身是一個引用.是可以改變的.陣列本身就是一個容器.
public static <T> Collector<T, ?, Integer>
summingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new int[1],
(a, t) -> { a[0] += mapper.applyAsInt(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
- averagingInt(),Long(),Double
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);
}
- 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);
}
- groupingBy()方法的實現.(不支援併發)
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());//呼叫下面2個引數的過載和toList()方法
}
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);//呼叫下面的三個引數的過載
}
downstream下游. (接受一個,返回一個. 返回的就叫下游)
T:分類器函式,輸入引數的型別.
K:分類器函式,返回的結果的型別.
D:返回的值的結果的型別.
HashMap::new :就是返回給客戶的Map/
好處:為了給使用者更好的使用.直接返回HashMap
壞處:侷限了只能返回HashMap型別.
//groupBy函式的最底層實現.
/**
* 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.
*
* @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)) {
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);
}
}
引數分析:
1.分類器: 輸入T型別,返回K型別 返回的Map的鍵,是K型別.
2.容器:HashMap
3.下游收集器: D為下游收集器的返回的型別.
方法邏輯分析.
- groupingByConcurrent() :(支援併發) (前提是你需要對順序沒有要求.)
public static <T, K>
Collector<T, ?, ConcurrentMap<K, List<T>>>
groupingByConcurrent(Function<? super T, ? extends K> classifier) {
return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList());
}
//ConcurrentHashMap 實現起來支援併發.
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);
}
}
- 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);
}
}
自己提供的內部靜態類:
/**
* Implementation class used by partitioningBy.
*/
private static final class Partition<T>
extends AbstractMap<Boolean, T>
implements Map<Boolean, T> {
final T forTrue;
final T forFalse;
Partition(T forTrue, T forFalse) {
this.forTrue = forTrue;
this.forFalse = forFalse;
}
@Override
public Set<Map.Entry<Boolean, T>> entrySet() {
return new AbstractSet<Map.Entry<Boolean, T>>() {
@Override
public Iterator<Map.Entry<Boolean, T>> iterator() {
Map.Entry<Boolean, T> falseEntry = new SimpleImmutableEntry<>(false, forFalse);
Map.Entry<Boolean, T> trueEntry = new SimpleImmutableEntry<>(true, forTrue);
return Arrays.asList(falseEntry, trueEntry).iterator();
}
@Override
public int size() {
return 2;
}
};
}
}
...
Stream類
public interface Stream<T> extends BaseStream<T, Stream<T>> {}
BaseStream類
package java.util.stream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.IntConsumer;
import java.util.function.Predicate;
/**
* Base interface for streams, which are sequences of elements supporting
* sequential and parallel aggregate operations. The following example
* illustrates an aggregate operation using the stream types {@link Stream}
* and {@link IntStream}, computing the sum of the weights of the red widgets:
*
* <pre>{@code
* int sum = widgets.stream()
* .filter(w -> w.getColor() == RED)
* .mapToInt(w -> w.getWeight())
* .sum();
* }</pre>
*
* See the class documentation for {@link Stream} and the package documentation
* for <a href="package-summary.html">java.util.stream</a> for additional
* specification of streams, stream operations, stream pipelines, and
* parallelism, which governs the behavior of all stream types.
*
* @param <T> the type of the stream elements
* @param <S> the type of the stream implementing {@code BaseStream}
* @since 1.8
* @see Stream
* @see IntStream
* @see LongStream
* @see DoubleStream
* @see <a href="package-summary.html">java.util.stream</a>
*/
public interface BaseStream<T, S extends BaseStream<T, S>>
extends AutoCloseable {
/**
* Returns an iterator for the elements of this stream.
*
* <p>This is a <a href="package-summary.html#StreamOps">terminal
* operation</a>.
*
* @return the element iterator for this stream
*/
Iterator<T> iterator();
/**
* Returns a spliterator for the elements of this stream.
*
* <p>This is a <a href="package-summary.html#StreamOps">terminal
* operation</a>.
*
* @return the element spliterator for this stream
*/
Spliterator<T> spliterator();
/**
* Returns whether this stream, if a terminal operation were to be executed,
* would execute in parallel. Calling this method after invoking an
* terminal stream operation method may yield unpredictable results.
*
* @return {@code true} if this stream would execute in parallel if executed
*/
boolean isParallel();
/**
* Returns an equivalent stream that is sequential. May return
* itself, either because the stream was already sequential, or because
* the underlying stream state was modified to be sequential.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @return a sequential stream
*/
S sequential();
/**
* Returns an equivalent stream that is parallel. May return
* itself, either because the stream was already parallel, or because
* the underlying stream state was modified to be parallel.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @return a parallel stream
*/
S parallel();
/**
* Returns an equivalent stream that is
* <a href="package-summary.html#Ordering">unordered</a>. May return
* itself, either because the stream was already unordered, or because
* the underlying stream state was modified to be unordered.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @return an unordered stream
*/
S unordered();
/**
* Returns an equivalent stream with an additional close handler. Close
* handlers are run when the {@link #close()} method
* is called on the stream, and are executed in the order they were
* added. All close handlers are run, even if earlier close handlers throw
* exceptions. If any close handler throws an exception, the first
* exception thrown will be relayed to the caller of {@code close()}, with
* any remaining exceptions added to that exception as suppressed exceptions
* (unless one of the remaining exceptions is the same exception as the
* first exception, since an exception cannot suppress itself.) May
* return itself.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param closeHandler A task to execute when the stream is closed
* @return a stream with a handler that is run if the stream is closed
*/
S onClose(Runnable closeHandler);
/**
* Closes this stream, causing all close handlers for this stream pipeline
* to be called.
*
* @see AutoCloseable#close()
*/
@Override
void close();
}
擴充套件:AutoCloseable介面
package java.lang;
/**
* An object that may hold resources (such as file or socket handles)
* until it is closed. The {@link #close()} method of an {@code AutoCloseable}
* object is called automatically when exiting a {@code
* try}-with-resources block for which the object has been declared in
* the resource specification header. This construction ensures prompt
* release, avoiding resource exhaustion exceptions and errors that
* may otherwise occur.
一個物件在關閉之前,會持有一些資源. 控制代碼之類的.
在退出塊的時候,會自動呼叫close()
避免資源被耗盡等異常.
*
* @apiNote
* <p>It is possible, and in fact common, for a base class to
* implement AutoCloseable even though not all of its subclasses or
* instances will hold releasable resources. For code that must operate
* in complete generality, or when it is known that the {@code AutoCloseable}
* instance requires resource release, it is recommended to use {@code
* try}-with-resources constructions. However, when using facilities such as
* {@link java.util.stream.Stream} that support both I/O-based and
* non-I/O-based forms, {@code try}-with-resources blocks are in
* general unnecessary when using non-I/O-based forms.
*
* @author Josh Bloch
* @since 1.7
*/
public interface AutoCloseable {
/**
* Closes this resource, relinquishing any underlying resources.
* This method is invoked automatically on objects managed by the
* {@code try}-with-resources statement.
*
* <p>While this interface method is declared to throw {@code
* Exception}, implementers are <em>strongly</em> encouraged to
* declare concrete implementations of the {@code close} method to
* throw more specific exceptions, or to throw no exception at all
* if the close operation cannot fail.
*
* <p> Cases where the close operation may fail require careful
* attention by implementers. It is strongly advised to relinquish
* the underlying resources and to internally <em>mark</em> the
* resource as closed, prior to throwing the exception. The {@code
* close} method is unlikely to be invoked more than once and so
* this ensures that the resources are released in a timely manner.
* Furthermore it reduces problems that could arise when the resource
* wraps, or is wrapped, by another resource.
*
* <p><em>Implementers of this interface are also strongly advised
* to not have the {@code close} method throw {@link
* InterruptedException}.</em>
*
* This exception interacts with a thread's interrupted status,
* and runtime misbehavior is likely to occur if an {@code
* InterruptedException} is {@linkplain Throwable#addSuppressed
* suppressed}.
*
* More generally, if it would cause problems for an
* exception to be suppressed, the {@code AutoCloseable.close}
* method should not throw it.
*
* <p>Note that unlike the {@link java.io.Closeable#close close}
* method of {@link java.io.Closeable}, this {@code close} method
* is <em>not</em> required to be idempotent. In other words,
* calling this {@code close} method more than once may have some
* visible side effect, unlike {@code Closeable.close} which is
* required to have no effect if called more than once.
*
* However, implementers of this interface are strongly encouraged
* to make their {@code close} methods idempotent.
*
* @throws Exception if this resource cannot be closed
*/
void close() throws Exception;
}
使用Example去理解這個介面
public class AutoCloseableTest implements AutoCloseable {
public static void main(String[] args) {
try(AutoCloseableTest autoCloseableTest = new AutoCloseableTest()) {
autoCloseableTest.doSomething();
} catch (Exception e) {
e.printStackTrace();
} //這種寫法.try with source.
}
@Override
public void close() throws Exception {
System.out.println("close invoked");
}
public void doSomething(){
System.out.println("doSomething invoked");
}
}
執行結果: (實現了這個介面的類,會自動執行 close()方法.)
總結:
- JDK內建的函式式介面在這裡得以體現.
看底層的原因:
不是因為要讓你開發過程中去
看了原始碼之後,你使用的時候的信心就非常足.
在遇到問題的時候,你能快速的將問題fix掉.
學習方法
1.看優秀的程式碼
2.去學習別人的東西
3.用的多了就會變成自己的東西.
附加一個小插曲
相關推薦
JAVA8學習——深入Comparator&Collector(學習過程)
深入Comparator&Collector 從原始碼深入Comparator Comparator從Java1.2就出來了,但是在1.8的時候,又添加了大量的預設方法. compare() equals() reversed() //倒序 thenComparing(Comparator<?
學習連結:python&web(待更新)
目錄: 一、python 與 web伺服器 一、python 與 web伺服器 1、深入淺出web伺服器與python應用程式之間的聯絡: https://juejin.im/entry/59d574c5f265da0666416e53 2、從
深入學習理論:VC維(VC dimensions)
上面這個式子就是模型的評估與選擇這篇文章中提到的泛化誤差上界。(喜極而泣,大費周章,終於把這個坑給填了)vc維在這裡面起到了一個懲罰項的作用,它所表徵的是模型的複雜程度,當模型越複雜的時候,vc維越大,泛化能力就越差;當模型越簡單的時候,vc維越小,經驗損失函式和期望損失函式越接近,泛化能力越好。
深入JAVA註解-Annotation(學習過程)
JAVA註解-Annotation學習 本文目的:專案開發過程中遇到自定義註解,想要弄清楚其原理,但是自己的基礎知識不足以支撐自己去探索此問題,所以先記錄問題,然後補充基礎知識,然後解決其問題。記錄此學習過程。 專案中遇到的註解: //使用註解的地方 @ServiceScan({"com.sin
JAVA8學習——Stream底層的實現(學習過程)
Stream底層的實現 Stream介面實現了 BaseStream 介面,我們先來看看BaseStream的定義 BaseStream BaseStream是所有流的父類介面。 對JavaDoc做一次解讀,瞭解提供的所有方法。 /** * Base interface for streams, which
學習MVC之租房網站(十二)-緩存和靜態頁面
.html 控制臺 ron 在線教育 適合 取代 system caching 租房網站 在上一篇<學習MVC之租房網站(十一)-定時任務和雲存儲>學習了Quartz的使用、發郵件,並將通過UEditor上傳的圖片保存到雲存儲。在項目的最後,再學習優化網站性能的
從零單排入門機器學習:線性回歸(linear regression)實踐篇
class rom enter instr function ont 線性 gin 向量 線性回歸(linear regression)實踐篇 之前一段時間在coursera看了Andrew ng的機器學習的課程,感覺還不錯,算是入門了。這次打算以該課程的作業
Python學習之路——第二彈(認識python)
內容 代碼結構 計算 戰術 個人 方法 十分 現在 目的 第一彈中我是說明了學習python的目的,主要為了自我提升的考慮,那麽為什麽我對python感興趣,python有什麽用了?本章就簡單說明下。 python的用途很廣,而且代碼十分簡潔,不像java、c等其他
python學習第十四節(正則)
image all flags 正則 asdf alt afa images lag python2和python3都有兩種字符串類型strbytes re模塊find一類的函數都是精確查找。字符串是模糊匹配 findall(pattern,string,flags) r
jquery-mobile 學習筆記之中的一個(基礎屬性)
obi 隱藏 ... ole his pan sin download 它的 寫在前面 本文是依據w3c 學習軌跡,自己研習過程中記錄下的筆記,僅僅供自己學習軌跡記錄之用,不喜勿噴。 0 引入庫 引入相應的文件: <link rel="stylesheet
Java 設計模式學習筆記1——策略模式(Duck例子)
利用 實例化 top 而是 實現 學習筆記 left ng- 多個 0、假設現有工程(Duck)中遇到為類添加功能的問題,如何設計類添加新的功能? 1、利用繼承提供的Duck(鴨子)的行為會導致哪些缺點? (1)代碼在多個子類中重復 (2)很多男知道所有鴨子的全部行為
大數據Hadoop學習之搭建Hadoop平臺(2.1)
穩定版 發的 log tar sshd scheduler 文件夾 三種 rest 關於大數據,一看就懂,一懂就懵。 一、簡介 Hadoop的平臺搭建,設置為三種搭建方式,第一種是“單節點安裝”,這種安裝方式最為簡單,但是並沒有展示出Hadoop的技術優勢,適
python學習_day69_django入門項目(老師表)
兩個 exit 關閉 python學習 one result utf8 row 入門 一、模板語言 二、多選框後端取值 後端取值方式如下: 三、ajax發數據方式 1、正常情況: 前端發數據: 後端處理數據: 2、非正常情況: 前端發數據: 後端
機器學習三--分類--鄰近取樣(Nearest Neighbor)
post 個數 均衡 urn learning clas 根據 () end 最鄰近規則分類 K-Nearest Neighbor 步驟: 1、為了判斷未知實例的類別,以所有已知類別的實例作為參考。 2、選擇參數K。 3、計算未知實例與所有已知實例的距離。
深度學習知識點查漏補缺(反向傳播)
普通 就是 post 神經網絡 節點 深度學習 網絡 非線性 關系 神經網絡反向傳播 首先理解一個基礎前提,神經網絡只是一個嵌套的,非線性函數(激活函數)復合線性函數的函數。對其優化,也同一般機器學習算法的目標函數優化一樣,可以用梯度下降等算法對所有函數參數進行優化。 但因
Spring boot 學習六 spring 繼承 mybatis (基於註解)
oot provide rom 構造 per 來看 color 如何使用 語言 MyBatis提供了多個註解如:@InsertProvider,@UpdateProvider,@DeleteProvider和@SelectProvider,這些都是建立動態語言和讓MyBat
機器學習課程不完全收錄(持續更新)
google 趨勢 con ash color mooc tar 業界 積極 機器學習已經火遍全球了,目前幾乎所有科技公司都或多或少都在積極地響應AI的趨勢,但是此時需要培養更多的人工智能和機器學習專家,然而優質的學習資源卻相當匱乏。幾大科技巨頭和業界領軍大神也相應提供了各
【深度學習】一文讀懂機器學習常用損失函數(Loss Function)
back and 們的 wiki 導出 歐氏距離 classes 自變量 關於 最近太忙已經好久沒有寫博客了,今天整理分享一篇關於損失函數的文章吧,以前對損失函數的理解不夠深入,沒有真正理解每個損失函數的特點以及應用範圍,如果文中有任何錯誤,請各位朋友指教,謝謝~
學習爬蟲看著篇(基礎篇)
src robots 利潤 請求響應 網絡數據 不能 聚焦 海量 音樂 1.爬蟲的定義 網絡爬蟲(又稱網絡蜘蛛)模擬客戶端發送網絡請求,接收請求響應,自動的進行抓取網絡數據的程度。 註意: 網絡爬蟲在進行抓取數據的時候並不能進行辨別信息真偽(比如某直播平臺直播房間顯示在線觀
學習筆記-C語言1(程式設計入門)
C語言和C++是作為一名程式設計師必備技能,非科班出身的我對這些語言一直是一知半解,後來更是直接使用簡單易上手的python,matlab語言。今天終於開始系統的學習了C了,記錄一些學習筆記,方便後面檢視,如有不妥,還請幫忙指正。 1. 檔案開頭 檔案開頭要加入:# include<