[英]Confused by Java8 Collectors.toMap
我有一個看起來像下面的集合,我想過濾除了幾個月結束之外的所有日期。
2010-01-01=2100.00,
2010-01-31=2108.74,
2010-02-01=2208.74,
2010-02-28=2217.92,
2010-03-01=2317.92,
2010-03-31=2327.57,
2010-04-01=2427.57,
2010-04-30=2437.67,
2010-05-01=2537.67,
2010-05-31=2548.22,
2010-06-01=2648.22,
2010-06-30=2659.24,
2010-07-01=2759.24,
2010-07-31=2770.72,
2010-08-01=2870.72,
2010-08-31=2882.66,
2010-09-01=2982.66,
2010-09-30=2995.07,
2010-10-01=3095.07,
2010-10-31=3107.94,
2010-11-01=3207.94,
2010-11-30=3221.29
我有以下過濾條件。 frequency.getEnd
返回LocalDate
匹配月底在給定LocalDate
。
.filter(p -> frequency.getEnd(p.getKey()) == p.getKey())
所以現在我想我必須將這個過濾后的流轉換回地圖。 而且我認為我使用收藏家來做到這一點。 因此我補充說:
.collect(Collectors.toMap(/* HUH? */));
但我不知道如何處理Collectors.toMap
。 閱讀的例子讓我很困惑。 這是我當前的代碼,顯然不起作用。
TreeMap<LocalDate, BigDecimal> values = values.entrySet()
.stream()
.filter(p -> frequency.getEnd(p.getKey()) == p.getKey())
.collect(Collectors.toMap(/* HUH? */));
考慮你的問題:你有一個地圖的入口流,也就是說一個Stream<Map.Entry<LocalDate, BigDecimal>>
,你想把它收集到TreeMap<LocalDate, BigDecimal>
。
所以,你是對的,你應該使用Collectors.toMap
。 現在,正如您在文檔中看到的 ,實際上有3個Collectors.toMap
,具體取決於參數:
toMap(keyMapper, valueMapper)
。 keyMapper
是一個函數,其輸入是流當前元素,其輸出是最終Map的鍵。 因此,它將Stream元素映射到鍵(因此名稱)。 valueMapper
是一個函數,其輸入是流當前元素,其輸出是最終Map的值。 toMap(keyMapper, valueMapper, mergeFunction)
。 前兩個參數與之前相同。 第三個是mergeFunction
,它是在最終Map中重復鍵元素時調用的函數; 因此,它的輸入是2個值(即keyMapper
返回相同鍵的兩個值)並將這兩個值合並為一個值。 toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)
。 前三個參數與之前相同。 第四個是Map的供應商:因為它當前在JDK中實現,前面的兩個toMap
返回一個HashMap
實例。 但是,如果您想要一個特定的Map實例,該供應商將返回該實例。 在我們的特定情況下,我們需要使用第三個toMap
,因為我們希望結果Map顯式地是TreeMap
。 讓我們看看我們應該給它什么輸入:
keyMapper
:所以這應該返回最終Map的鍵。 這里,我們正在處理Stream<Map.Entry<LocalDate, BigDecimal>>
因此每個Stream元素的類型為Map.Entry<LocalDate, BigDecimal>
。 然后,此函數將Map.Entry<LocalDate, BigDecimal> e
作為輸入。 它的輸出應該是最終Map的關鍵,在這種情況下,輸出應該是e.getKey()
,即條目所持有的LocalDate
。 這可以寫成lambda表達式: e -> e.getKey()
。 這也可以寫成方法引用Map.Entry::getKey
但是讓我們在這里堅持使用lambdas,因為它可能更容易理解。 valueMapper
:這與上面相同,但是,在這種情況下,此函數需要返回e.getValue()
,即條目所持有的BigDecimal
。 所以這是e -> e.getValue()
。 mergeFunction
:這是一個棘手的問題。 我們知道,通過構造,最終Map中沒有重復的鍵元素(即沒有重復的LocalDate
)。 我們在這里寫什么? 一個簡單的解決方案就是拋出一個異常:這不應該發生,如果確實如此,那么某個地方就會出現一個大問題。 所以無論兩個輸入參數如何,我們都會拋出異常。 這可以寫成(v1, v2) -> { throw new SomeException(); }
(v1, v2) -> { throw new SomeException(); }
。 請注意,它需要括在括號中。 在這種情況下,為了與JDK當前所做的一致,我選擇SomeException
為IllegalStateException
。 mapSupplier
:如前所述,我們想提供一個TreeMap
。 供應商不接受任何參數並返回新實例。 所以這可以寫成() -> new TreeMap<>()
。 同樣,我們可以使用方法引用並編寫TreeMap::new
。 最后的代碼,我剛剛編寫了Stream的收集部分(請注意,在此代碼中,您也可以使用相應的方法引用,如上所述,我在評論中添加了它們):
Collectors.toMap(
e -> e.getKey(), // Map.Entry::getKey
e -> e.getValue(), // Map.Entry::getValue
(v1, v2) -> { throw new IllegalStateException(); },
() -> new TreeMap<>()) // TreeMap::new
)
除了之前的答案,請注意,如果您不需要保留原始地圖,則可以在不使用Stream API的情況下就地執行此類過濾:
values.keySet().removeIf(k -> !frequency.getEnd(k).equals(k));
由於您正在迭代Map.Entry
值,而toMap()
只需要兩種方法來提取鍵和值,這很簡單:
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
請注意,這不會返回TreeMap
。 為此,您需要:
Collectors.toMap(Entry::getKey,
Entry::getValue,
(v1,v2) -> { throw new IllegalStateException("Duplicate key"); },
TreeMap::new)
最簡單形式的toMap方法有兩個參數:一個是將輸入映射到鍵的函數,另一個是將輸入映射到值的函數。 兩個函數的輸出組合在一起形成結果映射中的條目。
我想你需要做這樣的事情:
Collectors.toMap(p -> p.getKey(), p -> p.getValue())
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.