簡體   English   中英

Java8 Collectors.toMap困惑

[英]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當前所做的一致,我選擇SomeExceptionIllegalStateException
  • 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM