本章是該書的第五章, 主要講了方法引用和收集器java
形如:git
artist -> artist.getName() (String arg) -> arg.length()
這樣的表達式, 能夠簡寫爲:github
Artist::getName String::length
這種簡寫的語法被稱爲方法引用
. 方法引用無需考慮參數, 由於一個方法引用能夠在不一樣的狀況下解析爲不一樣的Lambda
表達式, 這依賴於JVM
的推斷.segmentfault
方法引用能夠分爲四類:安全
引用靜態方法: ClassName::staticMethodName
, 好比: String.valueOf
數據結構
引用特定實例方法: object::instanceMethodName
, 好比: str::toString併發
引用特定類型的任意對象的實例方法: ClassName::instanceMethodName
, 好比: String::length
app
引用構造方法: ClassName::new
, 好比: String::new
框架
當咱們對集合進行操做時, 有時但願是按照必定的順序來操做, 而有時又但願是亂序的操做. 有兩個方法能夠幫助咱們進行順序的操做.ide
BaseStream.unordered()
方法能夠打亂順序, 科技將原本有序的集合變成無序的集合
Stream.sorted
方法有兩個簽名, 一個無參, 一個有參數Comparator<? super T> comparator
無參的方法要求T
實現了Comparable
接口
有參方法須要提供一個比較器
收集器是一種通用的, 從流中生成複雜值的結構. 將其傳給collect
方法, 全部的流就均可以使用它. 而下面提到的單個收集器, 均可以使用reduce
方法模擬.
咱們可使用Collectors
中的靜態方法toList()
toSet()
等, 將流收集爲List
或Set
stream.collect(toList()) stream.collect(toSet())
咱們不須要關心具體使用的是哪種具體的實現, Stream
類庫會爲咱們選擇. 由於咱們能夠利用Stream
進行並行數據處理, 因此選擇是否線程安全的集合十分重要.
固然咱們也能夠指定使用哪種實現來進行收集:
stream.collect(toCollection(ArrayList::new))
Collectors
類提供了不少的方法用於轉化值, 好比counting
maxBy
minBy
等等, 能夠查看javadoc
瞭解.
目前瞭解到的是, 這三個方法均可以使用Stream
中的count
max
min
方法代替, 而不須要做爲collect
方法的參數
有時咱們想按照一個條件把數據分紅兩個部分, 而不是隻獲取符合條件的部分, 這時可使用partitioningBy
方法收集. 將它傳入collect
方法, 能夠獲得一個Map<Boolean, List>
, 而後就能夠對相應的數據進行處理了.
groupingBy
方法能夠將流分紅多個List
, 而不單單是兩個, 接收一個Lambda
表達式做爲參數, 其返回值做爲key
, 最後的結果也是一個Map
, 形如Map<String, List>
. 這一方法相似於SQL
中的group by
若是要從流中獲得字符串, 能夠在獲得Stream<String>
以後使用Collectors.joining
方法收集. 該方法接收3個String
參數, 分別是分隔符
前綴
後綴
artists.stream() .map(Artist::getName) .collect(Collectors.joining(",", "[", "]"));
咱們能夠將收集器組合起來, 達到更強的功能. 書上舉了兩個栗子
例一
public Map<Artist, Long> numberOfAlbums(Stream<Album> albums) { return albums .collect( groupingBy(Album::getMainMusicina, counting())); }
這個方法的目的是統計每一個歌手的做品數目. 若是不組合收集器, 咱們先用groupingBy
獲得一個Map<Artist, List<Album>>
以後, 還要去遍歷Map
獲得統計數目, 增長了代碼量和性能開銷.
上面的counting
方法相似於count
方法, 做用於List<Album>
的流上.
例二
public Map<Artist, List<String>> nameOfAlbums(Stream<Album> albums) { return albums .collect( groupingBy(Album::getMainMusician, mapping(Album::getName, toList()))); }
這個方法的目的是獲得每一個歌手的做品名稱列表. 若是不組合收集器, 咱們將會先獲得一個Map<Artist, List<Album>>
. 然而, 咱們只想獲得做品名稱, 也就是一個List<String>
, 組合mapping
收集器能夠幫助咱們實現效果.
mapping
收集器的功能相似於map
, 將一種類型的流轉換成另外一種類型. 因此相似的, mapping
並不知道要把結果收集成什麼數據結構, 它的第二個參數就會接收一個普通的收集器, 好比這裏的toList
, 來完成收集.
這裏的counting
和mapping
是咱們用到的第二個收集器, 用於收集最終結果的一個子集, 這些收集器叫作下游收集器
.
定製收集器看起來麻煩, 其實抓住要點就好了.
reduce
方法前面說過, 這些收集器均可以使用reduce
方法實現, 咱們定製收集器, 實際上就是爲reduce
方法編寫三個參數, 分別是:
identity
accumulator
combiner
關於這三個參數的意義, 若是不太理解, 能夠看看這個答案: https://segmentfault.com/q/1010000004944450
咱們能夠設計一個類, 爲這三個參數設計三個方法, 再提供一個方法用於獲取目標類型(若是這個類就是目標類型的話, 能夠不提供這個方法)
Collector
接口若是不想顯式的使用reduce
方法, 咱們只須要提供一個類, 實現Collector
接口.
該接口須要三個泛型參數, 依次是:
待收集元素的類型
累加器的類型
最終結果的類型
須要實現的方法有:
supplier
: 生成初始容器
accumulator
: 累加計算方法
combiner
: 在併發流中合併容器
finisher
: 將容器轉換成最終值
characteristics
: 獲取特徵集合
多數狀況下, 咱們的容器器和咱們的目標類型並不一致, 這時, 須要實現finisher
方法將容器轉化爲目標類型, 好比調用容器的toString
方法.
有時咱們的目標類型就是咱們的容器, finisher
方法就不須要對容器作任何操做, 而是經過設置characteristics
爲IDENTITY_FINISH
, 使用框架提供的優化獲得結果.
詳細講解能夠參見http://irusist.github.io/2016/01/04/Java-8%E4%B9%8BCollector/
Map
新增方法Java 8
爲Map
新增了不少方法, 能夠經過搜索引擎輕鬆找到相關文章. 這裏舉幾個書中提到的相關方法.
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
V computeIfPresent(K key, BiFunction<? super K, ? super V, extends V> remappingFunction)
V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
這三個方法相似, 都是根據key
來處理, 只是Lambda
表達式的執行條件不一樣, 從函數名就能夠看出來. 不過要注意Lambda
表達式的參數, 第一個方法的Lambda
只須要一個參數key
, 後面兩個方法的Lambda
須要兩個參數key
和value
, 而compute
方法的Lambda
中的value
參數可能爲null
.
V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
此方法用於合併value
, 新value
在第二個參數給出. Lambda
表達式規定合併方法, 其兩個參數依次是oldValue
和newValue
, oldValue
是原Map
的value
, 可能爲空; newValue
爲merge
方法的第二個參數.
void forEach(BiConsumer<? super K, ? super V> action)
經過forEach
方法, 再也不須要使用外部迭代來遍歷Map
.