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方法就是一個末端的操作。
通常,處理一個流涉及了這些步驟:
- 從某個源頭獲得一個流。
- 執行一個或更多的中間的操作。
- 執行一個末端的操作。
可能你想在一個方法中執行所有那些步驟。那樣的話,你就要知道源頭和流的屬性,而且要可以保證它被正確的使用。你可能不想接受任意的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));
更多內容稍后再補。