Java8之Stream-函数式接口
實習前只是粗略的看了下Java8的一些基本語法,但是沒有系統(tǒng)的學習過.在使用一段時間后決定系統(tǒng)的對其進行一次分析,加深對Java8函數(shù)式編程的理解,提高自己的編碼技巧.另外kotlin崛起,感興趣的朋友嘗試下混編也未嘗不可.
函數(shù)式接口
函數(shù)式接口,對于Java來說就是接口內(nèi)只有一個公開方法的接口,因為使用lanbda表達式,例如() -> user.getName()對應的調(diào)用則可能是func.get(),編譯器會根據(jù)接口推斷所屬于的方法,如果有兩個則無法推斷.Java8提供了很多函數(shù)式接口,一般都使用注解@FunctionalInterface聲明,有必要了解如下一些函數(shù)式接口.
| Supplier | 無 | T | 接收一個T類型的值 |
| Consumer | T | 無 | 處理一個T類型的值 |
| BiConsumer | T,U | 無 | 處理T類型和U類型的值 |
| Predicate | T | boolean | 處理T類型的值,并返回true或者false. |
| ToIntFunction | T | int | 處理T類型的值,并返回int值 |
| ToLongFunction | T | long | 處理T類型的值,并返回long值 |
| ToDoubleFunction | T | double | 處理T類型的值,并返回double值 |
| Function | T | R | 處理T類型的值,并返回R類型值 |
| BiFunction | T,U | R | 處理T類型和U類型的值,并返回R類型值 |
| BiFunction | T,U | R | 處理T類型和U類型的值,并返回R類型值 |
| UnaryOperator | T | T | 處理T類型值,并返回T類型值, |
| BinaryOperator | T,T | T | 處理T類型值,并返回T類型值 |
以上的函數(shù)每一個代表的都是一種基本的操作,操作之間可以自由組合,所以才有了stream這些靈活的操作.
Stream操作
Stream的操作是建立在函數(shù)式接口的組合上的,最好的學習方法是看Stream接口來學習.下面舉一些例子來分析,假設有這樣的一些初始數(shù)據(jù).
List<String> testData = new ArrayList<String>();testData.add("張三");testData.add("李四");testData.add("王二");testData.add("麻子");復制代碼filter
Stream<T> filter(Predicate<? super T> predicate);復制代碼filter接收predicate函數(shù),predicate是接收T值,返回boolean值,那么對應的引用就可以寫成如下形式,意思是取集合中以'張'開頭的名字.
testData.stream().filter(x -> x.startsWith("張"))復制代碼map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);復制代碼map操作接收的是Function接口,對于Function接收T值返回R值,那map的作用就很明顯是轉換用的,比如下面代碼,轉換名稱為對應的名稱長度,也就是從輸入String數(shù)據(jù)返回int數(shù)據(jù).
testData.stream().map(x -> x.length())復制代碼flatMap
<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());//輸出 [張, 三, 李, 四, 王, 二, 麻, 子]復制代碼peek
Stream<T> peek(Consumer<? super T> action);復制代碼peek參數(shù)為Consumer,Consumer接收T值,無返回,那么該方法就可以作為調(diào)試不影響stream中內(nèi)容的一些操作,不過由于對象都是地址引用,你再此做一些對象內(nèi)容操作也是可以的.
reduce
Reduce比較復雜的一個接口,屬于歸納性操作,看參數(shù),第一個是U泛型,也就是輸入類型的參數(shù),最為初始值,第二個BiFunction,接收T,U參數(shù),返回U類型參數(shù),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,然后兩個參數(shù)r即是identity,x為每次輸入?yún)?shù),最后一個StringBuilder::append是并發(fā)下多個identity的合并策略.
再舉個例子,既然reduce屬于歸納性操作,那么也可以當成collect使用,如下:
強大的collect
collect無疑是stream中最強大的操作,掌握了collect操作才能說掌握了stream.為了便于使用者,Java提供了Collectors類,該類提供了很多便捷的collect操作,如Collector<T, ?, List<T>> toList(),Collector<T, ?, Set<T>> toSet()等操作.這些操作最終都會調(diào)用如下構造函數(shù)構造出collector對象,因此掌握該本質(zhì)是最佳的學習方式.
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,接收一個元數(shù)據(jù),BiConsumer則是操作數(shù)據(jù),BinaryOperator并發(fā)下聚合,finisher完成時的轉換操作,Set應該按照定義是優(yōu)化一些操作中的轉換.如下面的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的實現(xiàn)
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作為轉換函數(shù)提供了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));復制代碼其他還有很多方法,就不一一敘述,主要是了解這些接口,知道他所擁有的功能,以及組合的意義,即可很好的掌握Java中的函數(shù)式編程.
個人博客 mrdear.cn ,歡迎交流
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結
以上是生活随笔為你收集整理的Java8之Stream-函数式接口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringCloud(第 017 篇)
- 下一篇: gdb日常使用