對於中間操做和終端操做的定義,請看《JAVA8 stream接口 中間操做和終端操做》,這篇主要講述的是stream的count,anyMatch,allMatch,noneMatch操做,咱們先看下函數的定義html
long count();
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
count方法,跟List接口的size同樣,返回的都是這個集合流的元素的長度,不一樣的是,流是集合的一個高級工廠,中間操做是工廠裏的每一道工序,咱們對這個流操做完成後,能夠進行元素的數量的和;java
剩下的三個方法,傳入的都是Predicate的函數式接口,接口定義請看《JAVA8 Predicate接口》;git
anyMatch表示,判斷的條件裏,任意一個元素成功,返回truegithub
allMatch表示,判斷條件裏的元素,全部的都是,返回true算法
noneMatch跟allMatch相反,判斷條件裏的元素,全部的都不是,返回trueexpress
下面,看幾個例子編程
List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
boolean aa = strs.stream().anyMatch(str -> str.equals("a"));
boolean bb = strs.stream().allMatch(str -> str.equals("a"));
boolean cc = strs.stream().noneMatch(str -> str.equals("a"));
long count = strs.stream().filter(str -> str.equals("a")).count();
System.out.println(aa);// TRUE
System.out.println(bb);// FALSE
System.out.println(cc);// FALSE
System.out.println(count);// 4
經過例子能夠看到,變量aa的表達式,strs裏的元素,任意有「a」,表示trueapi
變量bb的表達式,strs裏的元素,所有爲「a」,表示true,不然false併發
變量cc的表達式,strs裏的元素,所有不爲「a」,表示true,不然false
==============================================================oracle
java8的安裝
工欲善其器必先利其器,首先安裝JDK8。過程省略,你們應該均可以本身搞定。可是有一點這裏強調一下(Windows系統):目前咱們工做的版本通常是java 6或者java 7,因此不少人安裝java8基本都是學習爲主。這樣就在本身的機器上會存在多版本的JDK。並且你們通常是但願在命令行中執行java命令是基於老版本的jdk。可是在安裝完jdk8而且沒有設置path的狀況下,你若是在命令行中輸入:java -version,屏幕上會顯示是jdk 8。這是由於jdk8安裝的時候,會默認在C:/Windows/System32中增長java.exe,這個調用的優先級比path設置要高。因此即便path裏指定是老版本的jdk,可是執行java命令顯示的依然是新版本的jdk。這裏咱們要作的就是刪除C:/Windows/System32中的java.exe文件(不要手抖!)。
Lambda初體驗
下面進入本文的正題–lambda表達式。首先咱們看一下什麼是lambda表達式。如下是維基百科上對於」Lambda expression」的解釋:
a function (or a subroutine) defined, and possibly called, without being bound to an identifier。
簡單點說就是:一個不用被綁定到一個標識符上,而且可能被調用的函數。這個解釋還不夠通俗,lambda表達式能夠這樣定義(不精確,本身的理解):一段帶有輸入參數的可執行語句塊。這樣就比較好理解了吧?一例勝千言。有讀者反饋:不理解Stream的含義,因此這裏先提供一個沒用stream的lambda表達式的例子。
2 |
List<String> names = ...; |
3 |
Collections.sort(names, (o1, o2) -> o1.compareTo(o2)); |
2 |
List<String> names = ...; |
3 |
Collections.sort(names, new Comparator<String>() { |
5 |
public int compare(String o1, String o2) { |
6 |
return o1.compareTo(o2); |
上面兩段代碼分別是:使用lambda表達式來排序和使用匿名內部類來排序。這個例子能夠很明顯的看出lambda表達式簡化代碼的效果。接下來展現lambda表達式和其好基友Stream的配合。
1 |
List<String> names = new ArrayList<>(); |
4 |
List<String> lowercaseNames = names.stream().map((String name) -> { return name.toLowerCase();}).collect(Collectors.toList()); |
這段代碼就是對一個字符串的列表,把其中包含的每一個字符串都轉換成全小寫的字符串(熟悉Groovy和Scala的同窗確定會感受很親切)。注意代碼第四行的map方法調用,這裏map方法就是接受了一個lambda表達式(實際上是一個java.util.function.Function的實例,後面會介紹)。
爲何須要Lambda表達式呢?在嘗試回答這個問題以前,咱們先看看在Java8以前,若是咱們想作上面代碼的操做應該怎麼辦。
先看看普通青年的代碼:
1 |
List<String> names = new ArrayList<>(); |
4 |
List<String> lowercaseNames = new ArrayList<>(); |
5 |
for (String name : names) { |
6 |
lowercaseNames.add(name.toLowerCase()); |
接下來看看文藝青年的代碼(藉助Guava):
1 |
List<String> names = new ArrayList<>(); |
4 |
List<String> lowercaseNames = FluentIterable.from(names).transform( new Function<String, String>() { |
6 |
public String apply(String name) { |
7 |
return name.toLowerCase(); |
在此,咱們再也不討論普通青年和文藝青年的代碼風格孰優孰劣(有興趣的能夠去google搜索「命令式編程vs聲明式編程」)。本人更加喜歡聲明式的編程風格,因此偏好文藝青年的寫法。可是在文藝青年代碼初看起來看起來干擾信息有點多,Function匿名類的構造語法稍稍有點冗長。因此Java8的lambda表達式給咱們提供了建立SAM(Single Abstract Method)接口更加簡單的語法糖。
Lambda語法詳解
咱們在此抽象一下lambda表達式的通常語法:
1 |
(Type1 param1, Type2 param2, ..., TypeN paramN) -> { |
從lambda表達式的通常語法能夠看出來,仍是挺符合上面給出的非精確版本的定義–「一段帶有輸入參數的可執行語句塊」。
上面的lambda表達式語法能夠認爲是最全的版本,寫起來仍是稍稍有些繁瑣。彆着急,下面陸續介紹一下lambda表達式的各類簡化版:
1. 參數類型省略–絕大多數狀況,編譯器均可以從上下文環境中推斷出lambda表達式的參數類型。這樣lambda表達式就變成了:
1 |
(param1,param2, ..., paramN) -> { |
因此咱們最開始的例子就變成了(省略了List的建立):
1 |
List<String> lowercaseNames = names.stream().map((name) -> { return name.toLowerCase();}).collect(Collectors.toList()); |
2. 當lambda表達式的參數個數只有一個,能夠省略小括號。lambda表達式簡寫爲:
因此最開始的例子再次簡化爲:
1 |
List<String> lowercaseNames = names.stream().map(name -> { return name.toLowerCase();}).collect(Collectors.toList()); |
3. 當lambda表達式只包含一條語句時,能夠省略大括號、return和語句結尾的分號。lambda表達式簡化爲:
因此最開始的例子再次簡化爲:
1 |
List<String> lowercaseNames = names.stream().map(name -> name.toLowerCase()).collect(Collectors.toList()); |
4. 使用Method Reference(具體語法後面介紹)
2 |
List<String> lowercaseNames = names.stream().map(String::toLowerCase).collect(Collectors.toList()); |
Lambda表達式眼中的外部世界
咱們前面全部的介紹,感受上lambda表達式像一個閉關鎖國的傢伙,能夠訪問給它傳遞的參數,也能本身內部定義變量。可是卻歷來沒看到其訪問它外部的變量。是否是lambda表達式不能訪問其外部變量?咱們能夠這樣想:lambda表達式實際上是快速建立SAM接口的語法糖,原先的SAM接口均可以訪問接口外部變量,lambda表達式確定也是能夠(不但能夠,在java8中還作了一個小小的升級,後面會介紹)。
1 |
String[] array = { "a" , "b" , "c" }; |
2 |
for (Integer i : Lists.newArrayList( 1 , 2 , 3 )){ |
3 |
Stream.of(array).map(item -> Strings.padEnd(item, i, '@' )).forEach(System.out::println); |
上面的這個例子中,map中的lambda表達式訪問外部變量Integer i。而且能夠訪問外部變量是lambda表達式的一個重要特性,這樣咱們能夠看出來lambda表達式的三個重要組成部分:
不過lambda表達式訪問外部變量有一個很是重要的限制:變量不可變(只是引用不可變,而不是真正的不可變)。
1 |
String[] array = { "a" , "b" , "c" }; |
2 |
for ( int i = 1 ; i< 4 ; i++){ |
3 |
Stream.of(array).map(item -> Strings.padEnd(item, i, '@' )).forEach(System.out::println); |
上面的代碼,會報編譯錯誤。由於變量i被lambda表達式引用,因此編譯器會隱式的把其當成final來處理(ps:你們能夠想象問什麼上一個例子不報錯,而這個報錯。)細心的讀者確定會發現不對啊,之前java的匿名內部類在訪問外部變量的時候,外部變量必須用final修飾。Bingo,在java8對這個限制作了優化(前面說的小小優化),能夠不用顯示使用final修飾,可是編譯器隱式當成final來處理。
lambda眼中的this
在lambda中,this不是指向lambda表達式產生的那個SAM對象,而是聲明它的外部對象。
方法引用(Method reference)和構造器引用(construct reference)
方法引用
前面介紹lambda表達式簡化的時候,已經看過方法引用的身影了。方法引用能夠在某些條件成立的狀況下,更加簡化lambda表達式的聲明。方法引用語法格式有如下三種:
- objectName::instanceMethod
- ClassName::staticMethod
- ClassName::instanceMethod
前兩種方式相似,等同於把lambda表達式的參數直接當成instanceMethod|staticMethod的參數來調用。好比System.out::println等同於x->System.out.println(x);Math::max等同於(x, y)->Math.max(x,y)。
最後一種方式,等同於把lambda表達式的第一個參數當成instanceMethod的目標對象,其餘剩餘參數當成該方法的參數。好比String::toLowerCase等同於x->x.toLowerCase()。
構造器引用
構造器引用語法以下:ClassName::new,把lambda表達式的參數當成ClassName構造器的參數 。例如BigDecimal::new等同於x->new BigDecimal(x)。
吐槽一下方法引用
表面上看起來方法引用和構造器引用進一步簡化了lambda表達式的書寫,可是我的以爲這方面沒有Scala的下劃線語法更加通用。比較才能看出,翠花,上代碼!
1 |
List<String> names = new ArrayList<>(); |
4 |
names.stream().map(name -> name.charAt( 0 )).collect(Collectors.toList()); |
上面的這段代碼就是給定一個String類型的List,獲取每一個String的首字母,並將其組合成新的List。這段代碼就沒辦法使用方法引用來簡化。接下來,咱們簡單對比一下Scala的下劃線語法(沒必要太糾結Scala的語法,這裏只是作個對比):
2 |
List[String] names = .... |
在Scala中基本不用寫lambda表達式的參數聲明。
===========================================
1. Stream初體驗
咱們先來看看Java裏面是怎麼定義Stream的:
A sequence of elements supporting sequential and parallel aggregate operations.
咱們來解讀一下上面的那句話:
- Stream是元素的集合,這點讓Stream看起來用些相似Iterator;
- 能夠支持順序和並行的對原Stream進行匯聚的操做;
你們能夠把Stream當成一個高級版本的Iterator。原始版本的Iterator,用戶只能一個一個的遍歷元素並對其執行某些操做;高級版本的Stream,用戶只要給出須要對其包含的元素執行什麼操做,好比「過濾掉長度大於10的字符串」、「獲取每一個字符串的首字母」等,具體這些操做如何應用到每一個元素上,就給Stream就行了!(這個祕籍,通常人我不告訴他:))你們看完這些可能對Stream尚未一個直觀的認識,莫急,我們來段代碼。
2 |
List<Integer> nums = Lists.newArrayList( 1 , null , 3 , 4 , null , 6 ); |
3 |
nums.stream().filter(num -> num != null ).count(); |
上面這段代碼是獲取一個List中,元素不爲null的個數。這段代碼雖然很簡短,可是倒是一個很好的入門級別的例子來體現如何使用Stream,正所謂「麻雀雖小五臟俱全」。咱們如今開始深刻解刨這個例子,完成之後你可能能夠基本掌握Stream的用法!
1.1 剖析Stream通用語法
![](http://static.javashuo.com/static/loading.gif)
圖片就是對於Stream例子的一個解析,能夠很清楚的看見:本來一條語句被三種顏色的框分割成了三個部分。紅色框中的語句是一個Stream的生命開始的地方,負責建立一個Stream實例;綠色框中的語句是賦予Stream靈魂的地方,把一個Stream轉換成另一個Stream,紅框的語句生成的是一個包含全部nums變量的Stream,進過綠框的filter方法之後,從新生成了一個過濾掉原nums列表全部null之後的Stream;藍色框中的語句是豐收的地方,把Stream的裏面包含的內容按照某種算法來匯聚成一個值,例子中是獲取Stream中包含的元素個數。若是這樣解析之後,還不理解,那就只能動用「核武器」–圖形化,一圖抵千言!
![](http://static.javashuo.com/static/loading.gif)
在此咱們總結一下使用Stream的基本步驟:
- 建立Stream;
- 轉換Stream,每次轉換原有Stream對象不改變,返回一個新的Stream對象(**能夠有屢次轉換**);
- 對Stream進行聚合(Reduce)操做,獲取想要的結果;
2. 建立Stream
最經常使用的建立Stream有兩種途徑:
- 經過Stream接口的靜態工廠方法(注意:Java8裏接口能夠帶靜態方法);
- 經過Collection接口的默認方法(默認方法:Default method,也是Java8中的一個新特性,就是接口中的一個帶有實現的方法,後續文章會有介紹)–stream(),把一個Collection對象轉換成Stream
2.1 使用Stream靜態方法來建立Stream
1. of方法:有兩個overload方法,一個接受變長參數,一個接口單一值
1 |
Stream<Integer> integerStream = Stream.of( 1 , 2 , 3 , 5 ); |
2 |
Stream<String> stringStream = Stream.of( "taobao" ); |
2. generator方法:生成一個無限長度的Stream,其元素的生成是經過給定的Supplier(這個接口能夠當作一個對象的工廠,每次調用返回一個給定類型的對象)
1 |
Stream.generate( new Supplier<Double>() { |
7 |
Stream.generate(() -> Math.random()); |
8 |
Stream.generate(Math::random); |
三條語句的做用都是同樣的,只是使用了lambda表達式和方法引用的語法來簡化代碼。每條語句其實都是生成一個無限長度的Stream,其中值是隨機的。這個無限長度Stream是懶加載,通常這種無限長度的Stream都會配合Stream的limit()方法來用。
3. iterate方法:也是生成無限長度的Stream,和generator不一樣的是,其元素的生成是重複對給定的種子值(seed)調用用戶指定函數來生成的。其中包含的元素能夠認爲是:seed,f(seed),f(f(seed))無限循環
1 |
Stream.iterate( 1 , item -> item + 1 ).limit( 10 ).forEach(System.out::println); |
這段代碼就是先獲取一個無限長度的正整數集合的Stream,而後取出前10個打印。千萬記住使用limit方法,否則會無限打印下去。
2.2 經過Collection子類獲取Stream
這個在本文的第一個例子中就展現了從List對象獲取其對應的Stream對象,若是查看Java doc就能夠發現Collection接口有一個stream方法,因此其全部子類都均可以獲取對應的Stream對象。
1 |
public interface Collection<E> extends Iterable<E> { |
3 |
default Stream<E> stream() { |
4 |
return StreamSupport.stream(spliterator(), false ); |
3. 轉換Stream
轉換Stream其實就是把一個Stream經過某些行爲轉換成一個新的Stream。Stream接口中定義了幾個經常使用的轉換方法,下面咱們挑選幾個經常使用的轉換方法來解釋。
1. distinct: 對於Stream中包含的元素進行去重操做(去重邏輯依賴元素的equals方法),新生成的Stream中沒有重複的元素;
distinct方法示意圖(**如下全部的示意圖都要感謝[RxJava](https://github.com/Netflix/RxJava)項目的doc中的圖片給予的靈感, 若是示意圖表達的有錯誤和不許確的地方,請直接聯繫我。**):
![](http://static.javashuo.com/static/loading.gif)
2. filter: 對於Stream中包含的元素使用給定的過濾函數進行過濾操做,新生成的Stream只包含符合條件的元素;
filter方法示意圖:
![](http://static.javashuo.com/static/loading.gif)
3. map: 對於Stream中包含的元素使用給定的轉換函數進行轉換操做,新生成的Stream只包含轉換生成的元素。這個方法有三個對於原始類型的變種方法,分別是:mapToInt,mapToLong和mapToDouble。這三個方法也比較好理解,好比mapToInt就是把原始Stream轉換成一個新的Stream,這個新生成的Stream中的元素都是int類型。之因此會有這樣三個變種方法,能夠免除自動裝箱/拆箱的額外消耗;
map方法示意圖:
![](http://static.javashuo.com/static/loading.gif)
4. flatMap:和map相似,不一樣的是其每一個元素轉換獲得的是Stream對象,會把子Stream中的元素壓縮到父集合中;
flatMap方法示意圖:
![](http://static.javashuo.com/static/loading.gif)
5. peek: 生成一個包含原Stream的全部元素的新Stream,同時會提供一個消費函數(Consumer實例),新Stream每一個元素被消費的時候都會執行給定的消費函數;
peek方法示意圖:
![](http://static.javashuo.com/static/loading.gif)
6. limit: 對一個Stream進行截斷操做,獲取其前N個元素,若是原Stream中包含的元素個數小於N,那就獲取其全部的元素;
limit方法示意圖:
![](http://static.javashuo.com/static/loading.gif)
7. skip: 返回一個丟棄原Stream的前N個元素後剩下元素組成的新Stream,若是原Stream中包含的元素個數小於N,那麼返回空Stream;
skip方法示意圖:
![](http://static.javashuo.com/static/loading.gif)
8. 在一塊兒,在一塊兒!
1 |
List<Integer> nums = Lists.newArrayList( 1 , 1 , null , 2 , 3 , 4 , null , 5 , 6 , 7 , 8 , 9 , 10 ); |
2 |
System.out.println(「sum is:」+nums.stream().filter(num -> num != null ). |
3 |
distinct().mapToInt(num -> num * 2 ). |
4 |
peek(System.out::println).skip( 2 ).limit( 4 ).sum()); |
這段代碼演示了上面介紹的全部轉換方法(除了flatMap),簡單解釋一下這段代碼的含義:給定一個Integer類型的List,獲取其對應的Stream對象,而後進行過濾掉null,再去重,再每一個元素乘以2,再每一個元素被消費的時候打印自身,在跳過前兩個元素,最後去前四個元素進行加和運算(解釋一大堆,很像廢話,由於基本看了方法名就知道要作什麼了。這個就是聲明式編程的一大好處!)。你們能夠參考上面對於每一個方法的解釋,看看最終的輸出是什麼。
9. 性能問題
有些細心的同窗可能會有這樣的疑問:在對於一個Stream進行屢次轉換操做,每次都對Stream的每一個元素進行轉換,並且是執行屢次,這樣時間複雜度就是一個for循環裏把全部操做都作掉的N(轉換的次數)倍啊。其實不是這樣的,轉換操做都是lazy的,多個轉換操做只會在匯聚操做(見下節)的時候融合起來,一次循環完成。咱們能夠這樣簡單的理解,Stream裏有個操做函數的集合,每次轉換操做就是把轉換函數放入這個集合中,在匯聚操做的時候循環Stream對應的集合,而後對每一個元素執行全部的函數。
4. 匯聚(Reduce)Stream
匯聚這個詞,是我本身翻譯的,若是你們有更好的翻譯,能夠在下面留言。在官方文檔中是reduce,也叫fold。
在介紹匯聚操做以前,咱們先看一下Java doc中對於其定義:
A reduction operation (also called a fold) takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation, such as finding the sum or maximum of a set of numbers, or accumulating elements into a list. The streams classes have multiple forms of general reduction operations, called reduce() and collect(), as well as multiple specialized reduction forms such as sum(), max(), or count().
簡單翻譯一下:匯聚操做(也稱爲摺疊)接受一個元素序列爲輸入,反覆使用某個合併操做,把序列中的元素合併成一個彙總的結果。好比查找一個數字列表的總和或者最大值,或者把這些數字累積成一個List對象。Stream接口有一些通用的匯聚操做,好比reduce()和collect();也有一些特定用途的匯聚操做,好比sum(),max()和count()。注意:sum方法不是全部的Stream對象都有的,只有IntStream、LongStream和DoubleStream是實例纔有。
下面會分兩部分來介紹匯聚操做:
- 可變匯聚:把輸入的元素們累積到一個可變的容器中,好比Collection或者StringBuilder;
- 其餘匯聚:除去可變匯聚剩下的,通常都不是經過反覆修改某個可變對象,而是經過把前一次的匯聚結果當成下一次的入參,反覆如此。好比reduce,count,allMatch;
4.1 可變匯聚
可變匯聚對應的只有一個方法:collect,正如其名字顯示的,它能夠把Stream中的要有元素收集到一個結果容器中(好比Collection)。先看一下最通用的collect方法的定義(還有其餘override方法):
1 |
<R> R collect(Supplier<R> supplier, |
2 |
BiConsumer<R, ? super T> accumulator, |
3 |
BiConsumer<R, R> combiner); |
先來看看這三個參數的含義:Supplier supplier是一個工廠函數,用來生成一個新的容器;BiConsumer accumulator也是一個函數,用來把Stream中的元素添加到結果容器中;BiConsumer combiner仍是一個函數,用來把中間狀態的多個結果容器合併成爲一個(併發的時候會用到)。看暈了?來段代碼!
1 |
List<Integer> nums = Lists.newArrayList( 1 , 1 , null , 2 , 3 , 4 , null , 5 , 6 , 7 , 8 , 9 , 10 ); |
2 |
List<Integer> numsWithoutNull = nums.stream().filter(num -> num != null ). |
3 |
collect(() -> new ArrayList<Integer>(), |
4 |
(list, item) -> list.add(item), |
5 |
(list1, list2) -> list1.addAll(list2)); |
上面這段代碼就是對一個元素是Integer類型的List,先過濾掉所有的null,而後把剩下的元素收集到一個新的List中。進一步看一下collect方法的三個參數,都是lambda形式的函數(*上面的代碼可使用方法引用來簡化,留給讀者本身去思考*)。
- 第一個函數生成一個新的ArrayList實例;
- 第二個函數接受兩個參數,第一個是前面生成的ArrayList對象,二個是stream中包含的元素,函數體就是把stream中的元素加入ArrayList對象中。第二個函數被反覆調用直到原stream的元素被消費完畢;
- 第三個函數也是接受兩個參數,這兩個都是ArrayList類型的,函數體就是把第二個ArrayList所有加入到第一個中;
可是上面的collect方法調用也有點太複雜了,不要緊!咱們來看一下collect方法另一個override的版本,其依賴[Collector](http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html)。
1 |
<R, A> R collect(Collector<? super T, A, R> collector); |
這樣清爽多了!少年,還有好消息,Java8還給咱們提供了Collector的工具類–[Collectors](http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html),其中已經定義了一些靜態工廠方法,好比:Collectors.toCollection()收集到Collection中, Collectors.toList()收集到List中和Collectors.toSet()收集到Set中。這樣的靜態方法還有不少,這裏就不一一介紹了,你們能夠直接去看JavaDoc。下面看看使用Collectors對於代碼的簡化:
1 |
List<Integer> numsWithoutNull = nums.stream().filter(num -> num != null ). |
2 |
collect(Collectors.toList()); |
4.2 其餘匯聚
– reduce方法:reduce方法很是的通用,後面介紹的count,sum等均可以使用其實現。reduce方法有三個override的方法,本文介紹兩個最經常使用的,最後一個留給讀者本身學習。先來看reduce方法的第一種形式,其方法定義以下:
1 |
Optional<T> reduce(BinaryOperator<T> accumulator); |
接受一個BinaryOperator類型的參數,在使用的時候咱們能夠用lambda表達式來。
1 |
List<Integer> ints = Lists.newArrayList( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ); |
2 |
System.out.println( "ints sum is:" + ints.stream().reduce((sum, item) -> sum + item).get()); |
能夠看到reduce方法接受一個函數,這個函數有兩個參數,第一個參數是上次函數執行的返回值(也稱爲中間結果),第二個參數是stream中的元素,這個函數把這兩個值相加,獲得的和會被賦值給下次執行這個函數的第一個參數。要注意的是:**第一次執行的時候第一個參數的值是Stream的第一個元素,第二個參數是Stream的第二個元素**。這個方法返回值類型是Optional,這是Java8防止出現NPE的一種可行方法,後面的文章會詳細介紹,這裏就簡單的認爲是一個容器,其中可能會包含0個或者1個對象。
這個過程可視化的結果如圖:
![](http://static.javashuo.com/static/loading.gif)
reduce方法還有一個很經常使用的變種:
1 |
T reduce(T identity, BinaryOperator<T> accumulator); |
這個定義上上面已經介紹過的基本一致,不一樣的是:它容許用戶提供一個循環計算的初始值,若是Stream爲空,就直接返回該值。並且這個方法不會返回Optional,由於其不會出現null值。下面直接給出例子,就再也不作說明了。
1 |
List<Integer> ints = Lists.newArrayList( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ); |
2 |
System.out.println( "ints sum is:" + ints.stream().reduce( 0 , (sum, item) -> sum + item)); |
– count方法:獲取Stream中元素的個數。比較簡單,這裏就直接給出例子,不作解釋了。
1 |
List<Integer> ints = Lists.newArrayList( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ); |
2 |
System.out.println( "ints sum is:" + ints.stream().count()); |
– 搜索相關
– allMatch:是否是Stream中的全部元素都知足給定的匹配條件
– anyMatch:Stream中是否存在任何一個元素知足匹配條件
– findFirst: 返回Stream中的第一個元素,若是Stream爲空,返回空Optional
– noneMatch:是否是Stream中的全部元素都不知足給定的匹配條件
– max和min:使用給定的比較器(Operator),返回Stream中的最大|最小值
下面給出allMatch和max的例子,剩下的方法讀者當成練習。
1 |
List<Integer> ints = Lists.newArrayList( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ); |
2 |
System.out.println(ints.stream().allMatch(item -> item < 100 )); |
3 |
ints.stream().max((o1, o2) -> o1.compareTo(o2)).ifPresent(System.out::println); |