JDK1.8 關於stream接口的一系列流操作


java.util.stream

包匯總: java.util.stream

新的java.util.stream包提供了“支持在流上的函數式風格的值操作”(引用javadoc)的工具。

java.util.Stream 表示能應用在一組元素上一次執行的操作序列。Stream 操作分為中間操作或者最終操作兩種,最終操作返回一特定類型的計算結果,而中間操作返回Stream本身,這樣你就可以將多個操作依次串起來。活動流Stream 的創建需要指定一個數據源,比如 java.util.Collection的子類,List或者Set, Map不支持。

Java 8擴展了集合類,可以通過 Collection.stream() 或者 Collection.parallelStream() 來創建一個串行Stream或者並行Stream。

串行Stream如下所示:

Stream<T> stream = collection.stream();

 

一個流就像一個地帶器。這些值“流過”(模擬水流)然后他們離開。一個流可以只被遍歷一次,然后被丟棄。流也可以無限使用。

流Stream的操作可以是 串行執行  或者  並行執行。 它們可以使用其中一種方式開始,然后切換到另外的一種方式,使用stream.sequential()或stream.parallel()來達到這種切換。

串行Stream上的操作是在一個線程中依次完成,而並行Stream則是在多個線程上同時執行。

所以,你想用一個流來干什么?這里是在javadoc包里給出的例子:

int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED)
                                  .mapToInt(b -> b.getWeight())
                                  .sum();
 

注意,上面的代碼使用了一個原始的流,以及一個只能用在原始流上的sum()方法。下面馬上就會有更多關於原始流的細節:

 

流提供了流暢的API,可以進行數據轉換和對結果執行某些操作。流操作既可以是“中間的”也可以是“末端的”。

  • 中間的 -中間的操作保持流打開狀態,並允許后續的操作。上面例子中的filter和map方法就是中間的操作。這些操作的返回數據類型是流;它們返回當前的流以便串聯更多的操作。
  • 末端的 - 末端的操作必須是對流的最終操作。當一個末端操作被調用,流被“消耗”並且不再可用。上面例子中的sum方法就是一個末端的操作。

通常,處理一個流涉及了這些步驟:

  1. 從某個源頭獲得一個流。
  2. 執行一個或更多的中間的操作。
  3. 執行一個末端的操作。

可能你想在一個方法中執行所有那些步驟。那樣的話,你就要知道源頭和流的屬性,而且要可以保證它被正確的使用。你可能不想接受任意的Stream<T>實例作為你的方法的輸入,因為它們可能具有你難以處理的特性,比如並行的或無限的。

 

有幾個更普通的關於流操作的特性需要考慮:

  • 有狀態的 - 有狀態的操作給流增加了一些新的屬性,比如元素的唯一性,或者元素的最大數量,或者保證元素以排序的方式被處理。這些典型的要比無狀態的中間操作代價大。
  • 短路 - 短路操作潛在的允許對流的操作盡早停止,而不去檢查所有的元素。這是對無限流的一個特殊設計的屬性;如果對流的操作沒有短路,那么代碼可能永遠也不會終止。

對每個Sttream方法這里有一些簡短的,一般的描述。查閱javadoc獲取更詳盡的解釋。下面給出了每個操作的重載形式的鏈接。

中間的操作:

  • filter 1 - 排除所有與斷言不匹配的元素。
  • map 1 2 3 4 - 通過Function對元素執行一對一的轉換。
  • flatMap 1 2 3 4 5 - 通過FlatMapper將每個元素轉變為無或更多的元素。
  • peek 1 - 對每個遇到的元素執行一些操作。主要對調試很有用。
  • distinct 1 - 根據.equals行為排除所有重復的元素。這是一個有狀態的操作。
  • sorted 1 2 - 確保流中的元素在后續的操作中,按照比較器(Comparator)決定的順序訪問。這是一個有狀態的操作。
  • limit 1 - 保證后續的操作所能看到的最大數量的元素。這是一個有狀態的短路的操作。
  • substream 1 2 - 確保后續的操作只能看到一個范圍的(根據index)元素。像不能用於流的String.substring一樣。也有兩種形式,一種有一個開始索引,一種有一個結束索引。二者都是有狀態的操作,有一個結束索引的形式也是一個短路的操作。

末端的操作:

  • forEach 1 - 對流中的每個元素執行一些操作。
  • toArray 1 2 - 將流中的元素傾倒入一個數組。
  • reduce 1 2 3 - 通過一個二進制操作將流中的元素合並到一起。
  • collect 1 2 - 將流中的元素傾倒入某些容器,例如一個Collection或Map.
  • min 1 - 根據一個比較器找到流中元素的最小值。
  • max 1 -根據一個比較器找到流中元素的最大值。
  • count 1 - 計算流中元素的數量。
  • anyMatch 1 - 判斷流中是否至少有一個元素匹配斷言。這是一個短路的操作。
  • allMatch 1 - 判斷流中是否每一個元素都匹配斷言。這是一個短路的操作。
  • noneMatch 1 - 判斷流中是否沒有一個元素匹配斷言。這是一個短路的操作。
  • findFirst 1 - 查找流中的第一個元素。這是一個短路的操作。
  • findAny 1 - 查找流中的任意元素,可能對某些流要比findFirst代價低。這是一個短路的操作。

如 javadocs中提到的 , 中間的操作是延遲的(lazy)。只有末端的操作會立即開始流中元素的處理。在那個時刻,不管包含了多少中間的操作,元素會在一個傳遞中處理(通常,但並不總是)。(有狀態的操作如sorted() 和distinct()可能需要對元素的二次傳送。) 

那么接下來,簡介下相關流操作的使用:

測試對象A

1 public class A {
2     private int id;
3     private String userName;
4     private String sex;
5     private String job;
6     ..... 
7     ....//省略get和set方法 
8 }

一、代替for循環

在List<A> aList中,查找userName為hanmeimei的對象A。

1、查找集合中的第一個對象——filter

aList.stream() .filter(...) .findFirst()
1  Optional<A> firstA= aList.stream() .filter(a -> "hanmeimei".equals(a.getUserName())) .findFirst();

關於Optional,java API中給了解釋。 

1 //容器對象,該對象可以包含或不包含非空值。如果存在一個值,isPresent()將返回true,並且 get()將返回該值。
2 A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.

所以,我們可以這樣子使用:

1 if (firstA.isPresent()) {
2      A a = firstA.get();   //獲取對象
3 }else {
4    //沒有查到的邏輯
5 }

2、查找滿足條件的對象,並返回集合——filter、collect

aList.stream() .filter(...) .collect(Collectors.toList())
List<A> firstA= aList.stream() .filter(a -> "hanmeimei".equals(a.getUserName())) .collect(Collectors.toList());

 

3、對象列表 - > 字符串列表,即:獲取對象集合中所有的userName的集合。——map

  注意:stream().map( A::getXXX ) 等價於 stream().map(t -> t.getXXX)

aList.stream.map(...).collect(Collectors.toList())
1 //方法一
2 List<String> idList = aList.stream.map(A::getUserName).collect(Collectors.toList());
3 //方法二
4 List<String> collect = staff.stream().map(x -> x.getUserName()).collect(Collectors.toList());

 

4、對象集合 - > 其他對象集合 ,     此示例說明如何將 A對象集合轉換為 D對象集合。

 1 public static void main(String[] args) {
 2 
 3         List<A> voList= Arrays.asList(
 4                 new A("mkyong", "男", "醫生"),
 5                 new A("jack", "女", "護士"),
 6                 new A("lawrence", "女", "科學家")
 7         );
 8 
 9         // convert inside the map() method directly.
10         List<D> result = voList.stream().map(temp -> {
11             D obj = new D();
12             obj.setName(temp.getUserName());
13             obj.setSex(temp.getSex());
14             if ("mkyong".equals(temp.getUserName())) {
15                 obj.setExtra("this field is for mkyong only!");
16             }
17             return obj;
18  }).collect(Collectors.toList()); 19 
20         System.out.println(result);
21 
22     }

輸出結果為:
D{name='mkyong', sex="男", extra='this field is for mkyong only!'},
D{name='jack', sex="女", extra='null'},
D{name='lawrence', sex="女", extra='null'}

 二、List轉Map

id為key,A對象為value,可以這么做:

1 /**
2  * List -> Map
3  * 需要注意的是:
4  * toMap 如果集合對象有重復的key,會報錯Duplicate key ....
5  *  對象a1,對象a2的id都為1。
6  *  可以用 (k1,k2)->k1 來設置,如果有重復的key,則保留key1,舍棄key2
7  */
8 Map<Integer, A> aMap = aList.stream().collect(Collectors.toMap(A::getId, a -> a,(k1,k2)->k1));

更多內容稍后再補。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM