jdk1.8中stream相關操作

來源:包括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方法就是一個末端的操作。

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

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

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

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

對每個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));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章