stream and lambda(8) - 中間操作之篩選操作(filter、distinct、limit、skip)

語言: CN / TW / HK

Stream 流的資料處理過程中,少不的一點就是對資料的篩選。在以前的程式設計方式裡,對集合資料的篩選,要經過遍歷、判斷,搞不好過程中再遇到個異常,比如邊遍歷邊 remove。而 Stream 提供的資料篩選方法,簡化我們寫法的同時,也可以有效避免複雜編碼過程中不經意間寫的 bug。

filter

方法定義

Stream<T> filter(Predicate<? super T> predicate);

filter 方法,通過傳入的 Predicate 對流內的元素進行過濾,可以將不滿足條件的元素過濾掉。

關於 Predicate ,我們也在函式式介面的內容裡講過:傳送門

使用舉例

public void filterTest() {
    Stream<Integer> stream = Stream.of(1, 23, 4, 5, 6);
    List<Integer> result = stream.filter(i -> i > 5).collect(Collectors.toList());
    System.out.println(result);
}

執行結果如下,此時已將元素過濾:

[23, 6]

distinct

方法定義

Stream<T> distinct();

distinct 方法,無入參,呼叫後將流內的重複元素去除。

使用舉例

public void distinctTest() {
    Stream<Integer> stream = Stream.of(1, 1, 2, 2, 4);
    List<Integer> result = stream.distinct().collect(Collectors.toList());
    System.out.println(result);
}

執行結果如下:

[1, 2, 4]

元素到底是怎麼過濾的

雖然上面執行結果已經將元素過濾掉,但我們還是不清楚元素到底是怎麼被過濾掉的。如果是對自定義物件類的元素過濾,我們可能無法有效預測過濾結果。

經過原始碼檢視,看到如下片斷:

<P_IN> Node<T> reduce(PipelineHelper<T> helper, Spliterator<P_IN> spliterator) {
    TerminalOp<T, LinkedHashSet<T>> reduceOp
        = ReduceOps.<T, LinkedHashSet<T>>makeRef(LinkedHashSet::new, LinkedHashSet::add,
                                                     LinkedHashSet::addAll);
    return Nodes.node(reduceOp.evaluateParallel(helper, spliterator));
}

基本可以看出,去重是通過 Set 來去重的,而 Set 內部是通過 hashcodeequals 來去重。更詳細的內容,可以參考以前的文章: Java集合分析之Set-以HashSet為例

如果我們不想通過 hashcodeequals 去重怎麼辦呢?簡單,可以用 ConcurrentHashMap 記錄 key 是否已出現,配合 filter 方法來過濾。

limit

方法定義

Stream<T> limit(long maxSize);

limit 方法通過傳入留取的最大數,來過濾流中的元素。

使用舉例

public void limitTest() {
    Stream<Integer> stream = Stream.of(1, 3, 5, 7, 5);
    List<Integer> result = stream.limit(4).collect(Collectors.toList());
    System.out.println(result);
}

執行結果如下:

[1, 3, 5, 7]

skip

方法定義

Stream<T> skip(long n);

skip 方法通過傳入跳過的元素數量,來過濾流中的元素。和 limit 剛好組成一對,一個掐頭,一個去尾。

使用舉例

public void skipTest() {
    Stream<Integer> stream = Stream.of(1, 3, 5, 7, 5);
    List<Integer> result = stream.skip(3).collect(Collectors.toList());
    System.out.println(result);
}

執行結果如下:

[7, 5]

總結

  • 本次重點講了 Stream 中間操作的元素篩選部分。
  • skiplimit ,兩個操作一個掐頭,一個去尾。
  • filter 根據傳入的判斷條件來過濾元素。
  • distinct 根據 hashcodeequals 來去重。如果想根據其他內容來去重,可以使用 filter 來實現。

注:本文配套程式碼可在 github 檢視: stream-and-lambda