[英]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.