天天看點

java8 stream 操作java8 stream 操作

java8 stream 操作

Filter

Filter方法接收predicate接口參數,過濾流中所有元素。該操作屬于中間操作,是以可以在結果上繼續調用其他的流操作(如forEach)。ForEach接收consumer,在流過濾後的每個元素上執行consumer。ForEach是終止操作,傳回void,不能調用其他流操作。

stringCollection
    .stream()
    .filter((s) ->s.startsWith("a"))
    .forEach(System.out::println);

// "aaa2", "aaa1"
           

Sorted

Sorted是中間操作,傳回順序排列的流視圖,元素安裝預設的方式排序,除非你傳遞特定的比較器。

stringCollection
    .stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// "aaa1", "aaa2"
           

注意,sorted僅僅傳回流視圖,并沒有真正操作後面集合的順序,集合的順序沒有受任何影響。

System.out.println(stringCollection);

// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1
           

Map

map屬于中間操作,它通過傳遞的函數轉換每個元素。下面示例轉換每個字元串值大寫形式。也可以使用map轉換每個對象至另一種類型。傳回類型取決于傳遞函數的泛型類型。

stringCollection
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);
 

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
           

Match

幾個比對操作常用檢查指定predicate比對情況,所有比對操作都是終止操作,傳回布爾結果。

boolean anyStartsWithA =
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));
 

System.out.println(anyStartsWithA);      // true


boolean allStartsWithA =
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));
 

System.out.println(allStartsWithA);      // false

 
boolean noneStartsWithZ =
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));


System.out.println(noneStartsWithZ);      // true
           

Count

count是終止操作,傳回流中元素數量。

long startsWithB =
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();


System.out.println(startsWithB);    // 3
           

Reduce

terminal是終止操作,根據指定函數在流元素上執行reduction操作,傳回Optional對象,包括執行結果。

Optional<String> reduced =
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);

 
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
           

Parallel Streams

上面提到流可以是順序的或并行的;順序流操作是單線程執行,而并行流操作屬于多線程執行。下面示例示範使用并行流提升性能,非常簡單。首先建立list,包含大資料量的不重複元素。

int max = 1000000;

List<String> values = new ArrayList<>(max);

for (int i = 0; i < max; i++) {
    UUID uuid = UUID.randomUUID();
    values.add(uuid.toString());

}
           

現在我們測量針對該集合流排序所化的時間。

Sequential Sort

long t0 = System.nanoTime();

long count = values.stream().sorted().count();
System.out.println(count);


long t1 = System.nanoTime();

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);

System.out.println(String.format("sequential sort took: %d ms", millis));

 
// sequential sort took: 899 ms
           

Parallel Sort

long t0 = System.nanoTime();


long count = values.parallelStream().sorted().count();

System.out.println(count);

long t1 = System.nanoTime();

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);

System.out.println(String.format("parallel sort took: %d ms", millis));

// parallel sort took: 472 ms
           

可以看兩端代碼幾乎相同,但是并行流操作幾乎快樂50%。是以你當然要使用并行流代替順序流。

Map

前面提到,map不支援流。想法map提供了幾個新的、有用的方法實作通用任務。

Map<Integer, String> map = new HashMap<>();


for (int i = 0; i < 10; i++) {

    map.putIfAbsent(i, "val" + i);

}

map.forEach((id, val) -> System.out.println(val));
           
上面代碼不解自明,putIfAbsent無需我們寫額外的null檢查;forEach接收consumer針對map的每個值進行操作。示例看map的使用函數。      
map.computeIfPresent(3, (num, val) -> val + num);

map.get(3);             // val33

map.computeIfPresent(9, (num, val) -> null);
map.containsKey(9);     // false
map.computeIfAbsent(23, num -> "val" + num);
map.containsKey(23);    // true

map.computeIfAbsent(3, num -> "bam");

map.get(3);             // val33
           

接下來,我們學習如何根據指定的key删除entry,僅當正确的映射指定的值。

map.remove(3, "val3");

map.get(3);             // val33

map.remove(3, "val33");

map.get(3);             // null
           

另一個有用的方法:

map.getOrDefault(42, "not found");  // not found

Merging entries of a map is quite easy:

map.merge(9, "val9", (value, newValue) -> value.concat(newValue));

map.get(9);             // val9

map.merge(9, "concat", (value, newValue) -> value.concat(newValue));

map.get(9);             // val9concat
           

merge方法判斷,如果key沒有對應的entry,則key/value加入map,否則merge參數對應函數被調用改變對應的值。

繼續閱讀