困惑 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
returns 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? */));
由于您要迭代 Map.Entry
个值,而 toMap()
只需要两种方法来提取键和值,就这么简单:
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
请注意,这 不是 return 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())
这样考虑你的问题:你有一个映射的入口流,也就是说一个Stream<Map.Entry<LocalDate, BigDecimal>>
,你想把它收集到一个TreeMap<LocalDate, BigDecimal>
。
所以,你是对的,你应该使用Collectors.toMap
。现在,正如您在 the documentation 中看到的,实际上有 3 个 Collectors.toMap
,具体取决于参数:
toMap(keyMapper, valueMapper)
。 keyMapper
是一个函数,输入是流当前元素,输出是最终Map的key。因此,它将 Stream 元素映射到一个键(因此得名)。 valueMapper
是一个函数,输入是流当前元素,输出是最终Map的值。
toMap(keyMapper, valueMapper, mergeFunction)
。前两个参数与之前相同。第三个 mergeFunction
是在最终 Map 中出现重复键元素时调用的函数;因此,它的输入是 2 个值(即 keyMapper
return 编辑相同键的两个值)并将这两个值合并为一个值。
toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)
。前三个参数与之前相同。第四个是 Map 的供应商:因为它目前在 JDK 中实现,前两个 toMap
return 一个 HashMap
实例。但是如果你想要一个特定的地图实例,这个供应商会return那个实例。
在我们的特定情况下,我们需要使用第三个 toMap
,因为我们希望结果 Map 明确为 TreeMap
。让我们看看我们应该给它什么输入:
keyMapper
:所以这应该是return最终Map的key。在这里,我们处理的是 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
但我们在这里坚持使用 lambda,因为它可能更容易理解。
valueMapper
:这个和上面一样,但是,在这种情况下,这个函数需要returne.getValue()
,即入口是BigDecimal
保持。所以这是 e -> e.getValue()
.
mergeFunction
:这是一个棘手的问题。通过构造,我们知道最终的 Map 中没有重复的键元素(即没有重复的 LocalDate
)。我们在这里写什么?一个简单的解决方案是抛出一个异常:这不应该发生,如果发生了,那就是某个地方出了大问题。因此,无论两个输入参数是什么,我们都会抛出异常。这可以写成(v1, v2) -> { throw new SomeException(); }
。请注意,它需要用括号括起来。在这种情况下,为了与 JDK 当前所做的保持一致,我选择 SomeException
为 IllegalStateException
。
mapSupplier
:如前所述,我们要提供一个TreeMap
。供应商不接受任何参数,并且 return 是一个新实例。所以这里可以写成() -> 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));
我有一个如下所示的集合,我想过滤掉除月末以外的所有内容。
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
returns 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? */));
由于您要迭代 Map.Entry
个值,而 toMap()
只需要两种方法来提取键和值,就这么简单:
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
请注意,这 不是 return 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())
这样考虑你的问题:你有一个映射的入口流,也就是说一个Stream<Map.Entry<LocalDate, BigDecimal>>
,你想把它收集到一个TreeMap<LocalDate, BigDecimal>
。
所以,你是对的,你应该使用Collectors.toMap
。现在,正如您在 the documentation 中看到的,实际上有 3 个 Collectors.toMap
,具体取决于参数:
toMap(keyMapper, valueMapper)
。keyMapper
是一个函数,输入是流当前元素,输出是最终Map的key。因此,它将 Stream 元素映射到一个键(因此得名)。valueMapper
是一个函数,输入是流当前元素,输出是最终Map的值。toMap(keyMapper, valueMapper, mergeFunction)
。前两个参数与之前相同。第三个mergeFunction
是在最终 Map 中出现重复键元素时调用的函数;因此,它的输入是 2 个值(即keyMapper
return 编辑相同键的两个值)并将这两个值合并为一个值。toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)
。前三个参数与之前相同。第四个是 Map 的供应商:因为它目前在 JDK 中实现,前两个toMap
return 一个HashMap
实例。但是如果你想要一个特定的地图实例,这个供应商会return那个实例。
在我们的特定情况下,我们需要使用第三个 toMap
,因为我们希望结果 Map 明确为 TreeMap
。让我们看看我们应该给它什么输入:
keyMapper
:所以这应该是return最终Map的key。在这里,我们处理的是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
但我们在这里坚持使用 lambda,因为它可能更容易理解。valueMapper
:这个和上面一样,但是,在这种情况下,这个函数需要returne.getValue()
,即入口是BigDecimal
保持。所以这是e -> e.getValue()
.mergeFunction
:这是一个棘手的问题。通过构造,我们知道最终的 Map 中没有重复的键元素(即没有重复的LocalDate
)。我们在这里写什么?一个简单的解决方案是抛出一个异常:这不应该发生,如果发生了,那就是某个地方出了大问题。因此,无论两个输入参数是什么,我们都会抛出异常。这可以写成(v1, v2) -> { throw new SomeException(); }
。请注意,它需要用括号括起来。在这种情况下,为了与 JDK 当前所做的保持一致,我选择SomeException
为IllegalStateException
。mapSupplier
:如前所述,我们要提供一个TreeMap
。供应商不接受任何参数,并且 return 是一个新实例。所以这里可以写成() -> 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));