轉自:https://blog.csdn.net/u012706811/article/details/77096257併發
函數式接口,對於Java來講就是接口內只有一個公開方法的接口,由於使用lanbda表達式,例如() -> user.getName()對應的調用則多是func.get(),編譯器會根據接口推斷所屬於的方法,若是有兩個則沒法推斷.Java8提供了不少函數式接口,通常都使用註解@FunctionalInterface聲明,有必要了解以下一些函數式接口.app
以上的函數每個表明的都是一種基本的操做,操做之間能夠自由組合,因此纔有了stream這些靈活的操做.ide
Stream的操做是創建在函數式接口的組合上的,最好的學習方法是看Stream接口來學習.下面舉一些例子來分析,假設有這樣的一些初始數據.函數
List<String> testData = new ArrayList<String>();
testData.add("張三");
testData.add("李四");
testData.add("王二");
testData.add("麻子");學習
Stream<T> filter(Predicate<? super T> predicate);優化
filter接收predicate函數,predicate是接收T值,返回boolean值,那麼對應的引用就能夠寫成以下形式,意思是取集合中以’張’開頭的名字.ui
testData.stream()
.filter(x -> x.startsWith("張"))this
<R> Stream<R> map(Function<? super T, ? extends R> mapper);.net
map操做接收的是Function接口,對於Function接收T值返回R值,那map的做用就很明顯是轉換用的,好比下面代碼,轉換名稱爲對應的名稱長度,也就是從輸入String數據返回int數據.調試
testData.stream()
.map(x -> x.length())
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
flatMap和map都是使用Function接口,不一樣的是返回值flatMap限定爲Stream類型.因此flatMap能夠做爲合併流使用,如如下代碼,提取出全部的字符.
testData.stream()
.flatMap(x -> Stream.of(x.split("")))
.collect(Collectors.toList());
//輸出 [張, 三, 李, 四, 王, 二, 麻, 子]
Stream<T> peek(Consumer<? super T> action);
peek參數爲Consumer,Consumer接收T值,無返回,那麼該方法就能夠做爲調試不影響stream中內容的一些操做,不過因爲對象都是地址引用,你再此作一些對象內容操做也是能夠的.
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
Reduce比較複雜的一個接口,屬於概括性操做,看參數,第一個是U泛型,也就是輸入類型的參數,最爲初始值,第二個BiFunction,接收T,U參數,返回U類型參數,BinaryOperator接收U,U類型,並返回U類型.
StringBuilder identity = new StringBuilder();
StringBuilder reduce = testData.stream()
.flatMap(x -> Stream.of(x.split("")))
.reduce(identity, (r, x) -> {
r.append(x);
return r;
}, StringBuilder::append);
System.out.println(identity == reduce);
System.out.println(reduce.toString());
//輸出 true
// 張三李四王二麻子
首先提供一個基本容器identity,而後兩個參數r便是identity,x爲每次輸入參數,最後一個StringBuilder::append是併發下多個identity的合併策略.
再舉個例子,既然reduce屬於概括性操做,那麼也能夠當成collect使用,以下:
ArrayList<String> identity = new ArrayList<>();
ArrayList<String> result = testData.stream()
.flatMap(x -> Stream.of(x.split("")))
.reduce(identity, (r, x) -> {
r.add(x);
return r;
},(r1,r2) -> {
r1.addAll(r2);
return r1;
});
System.out.println(identity == result);
System.out.println(result);
//輸出 true
//[張, 三, 李, 四, 王, 二, 麻, 子]
collect無疑是stream中最強大的操做,掌握了collect操做才能說掌握了stream.爲了便於使用者,Java提供了Collectors類,該類提供了不少便捷的collect操做,如Collector<T, ?, List<T>> toList(),Collector<T, ?, Set<T>> toSet()等操做.這些操做最終都會調用以下構造函數構造出collector對象,所以掌握該本質是最佳的學習方式.
CollectorImpl(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A,R> finisher,
Set<Characteristics> characteristics) {
this.supplier = supplier;
this.accumulator = accumulator;
this.combiner = combiner;
this.finisher = finisher;
this.characteristics = characteristics;
}
Supplier相似reduce中的u,接收一個元數據,BiConsumer則是操做數據,BinaryOperator併發下聚合,finisher完成時的轉換操做,Set應該按照定義是優化一些操做中的轉換.以下面的toList()操做,其finish操做爲castingIdentity().
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);
}
再看toMap的實現
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {
BiConsumer<M, T> accumulator
= (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}
Function做爲轉換函數提供了key和value的轉換,BinaryOperator提供了重複key合併策略,mapSupplier則表示最終收集到的容器.那麼使用就很簡單了
HashMap<Character, String> map = testData.stream() .collect(Collectors.toMap(x -> x.charAt(0), Function.identity() , (v1, v2) -> v2, HashMap::new));