來源:包括jdk文檔,其他網友博客。做一個記錄和筆記:
java.util.stream
基本概念:
java.util.stream包提供了“支持在流上的函數式風格的值操作”的工具。文檔連接: 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方法就是一個末端的操作。
通常,處理一個流涉及了這些步驟:
- 從某個源頭獲得一個流。
- 執行一個或更多的中間的操作。
- 執行一個末端的操作。
有幾個更普通的關於流操作的特性需要考慮:
- 有狀態的 - 有狀態的操作給流增加了一些新的屬性,比如元素的唯一性,或者元素的最大數量,或者保證元素以排序的方式被處理。這些典型的要比無狀態的中間操作代價大。
- 短路 - 短路操作潛在的允許對流的操作儘早停止,而不去檢查所有的元素。這是對無限流的一個特殊設計的屬性;如果對流的操作沒有短路,那麼代碼可能永遠也不會終止。
對每個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](http://javadocs.techempower.com/jdk18/api/java/util/stream/Stream.html#substream(long, long)) - 確保後續的操作只能看到一個範圍的(根據index)元素。像不能用於流的String.substring一樣。也有兩種形式,一種有一個開始索引,一種有一個結束索引。二者都是有狀態的操作,有一個結束索引的形式也是一個短路的操作。
末端的操作:
- forEach 1 - 對流中的每個元素執行一些操作。
- toArray 1 2 - 將流中的元素傾倒入一個數組。
- reduce 1 [2](http://javadocs.techempower.com/jdk18/api/java/util/stream/Stream.html#reduce(T, java.util.function.BinaryOperator)) [3](http://javadocs.techempower.com/jdk18/api/java/util/stream/Stream.html#reduce(U, java.util.function.BiFunction, java.util.function.BinaryOperator)) - 通過一個二進制操作將流中的元素合併到一起。
- collect 1 [2](http://javadocs.techempower.com/jdk18/api/java/util/stream/Stream.html#collect(java.util.function.Supplier, java.util.function.BiConsumer, java.util.function.BiConsumer)) - 將流中的元素傾倒入某些容器,例如一個Collection或Map.
- min 1 - 根據一個比較器找到流中元素的最小值。
- max 1 -根據一個比較器找到流中元素的最大值。
- count 1 - 計算流中元素的數量。
- anyMatch 1 - 判斷流中是否至少有一個元素匹配斷言。這是一個短路的操作。
- allMatch 1 - 判斷流中是否每一個元素都匹配斷言。這是一個短路的操作。
- noneMatch 1 - 判斷流中是否沒有一個元素匹配斷言。這是一個短路的操作。
- findFirst 1 - 查找流中的第一個元素。這是一個短路的操作。
- findAny 1 - 查找流中的任意元素,可能對某些流要比findFirst代價低。這是一個短路的操作。
- orElse :不滿足條件時執行
如 javadocs中提到的 , 中間的操作是延遲的(lazy)。只有末端的操作會立即開始流中元素的處理。在那個時刻,不管包含了多少中間的操作,元素會在一個傳遞中處理(通常,但並不總是)。(有狀態的操作如sorted() 和distinct()可能需要對元素的二次傳送。)
示例1:
List<Person> persons = Arrays.asList(new Person("mkyong"),
new Person("michael"), new Person("lawrence"));
Person result = persons.stream() // Convert to steam
.filter(x -> "michael".equals(x.getName())) // we want "michael" only
.findAny() // If 'findAny' then return found
.orElse(null);
示例2:再使用HttpClient連接池,自定義keepAlive策略時:
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
return Arrays.asList(response.getHeaders(HTTP.CONN_KEEP_ALIVE))
.stream()
.filter(h -> StringUtils.equalsIgnoreCase(h.getName(), "timeout")
&& StringUtils.isNumeric(h.getValue()))
.findFirst()
.map(h -> NumberUtils.toLong(h.getValue(), DEFAULT_SECONDS))
.orElse(DEFAULT_SECONDS) * 1000;
}
一、代替for循環
在List aList中,查找userName爲zhangsan的對象A。
1、查找集合中的第一個對象——filter
aList.stream() .filter(...) .findFirst()
1 Optional<Person> firstPerson= aList.stream() .filter(a -> "zhangsan".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 (firstPerson.isPresent()) {
2 Person a = firstPerson.get(); //獲取對象
3 }else {
4 //沒有查到的邏輯
5 }
2、查找滿足條件的對象,並返回集合——filter、collect
aList.stream() .filter(...) .collect(Collectors.toList())
List<Person> firstPerson= aList.stream() .filter(a -> "zhangsan".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<Person> voList= Arrays.asList(
4 new Person("zhangsan", "男", "java開發"),
5 new Person("lisi", "女", "安卓"),
6 new Person("makie", "女", "go語言")
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 ("zhangsan".equals(temp.getUserName())) {
15 obj.setExtra("this field is for zhangsan only!");
16 }
17 return obj;
18 }).collect(Collectors.toList());
19
20 System.out.println(result);
21
22 }輸出結果爲:D{name='zhangsan', sex="男", extra='this field is for zhangsan only!'}, D{name='lisi', sex="女", extra='null'}, D{name='makie', 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, Person> aMap = aList.stream().collect(Collectors.toMap(A::getId, a -> a,(k1,k2)->k1));