Java8 Stream流的合併

xfcoding發表於2023-05-05

最近的需求裡有這樣一個場景,要校驗一個集合中每個物件的多個Id的有效性。比如一個Customer物件,有3個Id:id1id2id3,要把這些Id全部取出來,然後去資料庫裡查詢它是否存在。

@Data
@AllArgsConstructor
public class Customer {
    private String name;
    private String id1;
    private String id2;
    private String id3;
}

在通常情況下,我們要從集合中取出一個物件屬性,很好辦,用這個辦法:

customerList.stream().map(Customer::getId1).filter(Objects::nonNull).collect(Collectors.toList())

現在要取3個欄位,怎麼做呢?

Stream.concat

Stream介面中的靜態方法concat,可以把兩個流合成一個,我們取3個欄位可以合併兩次:

Stream<String> concat = Stream.concat(customerList.stream().map(Customer::getId1),
		customerList.stream().map(Customer::getId2));
List<String> ids = Stream.concat(concat, customerList.stream().map(Customer::getId3))
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

取4個欄位,就再繼續合併。但是這種不夠簡潔,可以使用扁平化流flatMap。

flatMap

flatmap方法讓你把一個流中的每個值都換成另一個流,然後把所有的流連線起來成為一個流。

Stream.flatMap方法的入參為一個Function函式,函式返回值的泛型要求為Stream型別。對比一下,mapflatMap都是將流中的元素對映為我們想要的值,只是flatMap對映的結果是一個新的Stream。

Stream.of方法剛好可以構建一個型別為Stream的原始流,以供flatMap操作。

List<String> ids = Stream.of(customerList.stream().map(Customer::getId1),
                             customerList.stream().map(Customer::getId2),
                             customerList.stream().map(Customer::getId3))
        .flatMap(idStream -> idStream)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

注意,Stream.of方法返回的流的泛型跟方法入參的型別是一樣,上面的程式碼就相當於,Stream.of(stream, stream, stream), 得到的結果就是Stream<Stream>,緊接著用flatMap扁平化處理,把每一個元素合成一個新流。

相關文章