java 被 Java8 Collectors.toMap 弄糊涂了
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33724733/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Confused by Java8 Collectors.toMap
提问by Patrick
I have a collection that looks like below, and I want to filter out the everything except the dates that aren't the end of the months.
我有一个如下所示的集合,我想过滤掉除月末日期以外的所有内容。
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
I have the following filter criteria. frequency.getEnd
returns a LocalDate
matching the end of the month for the given LocalDate
.
我有以下过滤条件。frequency.getEnd
返回LocalDate
匹配给定的月末LocalDate
。
.filter(p -> frequency.getEnd(p.getKey()) == p.getKey())
So now I think I have to converted this filtered stream back to a map. And I think I use a collector to do that. Thus I add:
所以现在我想我必须将这个过滤后的流转换回地图。我想我使用收集器来做到这一点。因此我补充说:
.collect(Collectors.toMap(/* HUH? */));
But I don't know what to do with Collectors.toMap
. Reading examples leaves me confused. Here's my current code which obviously doesn't work.
但我不知道该怎么办Collectors.toMap
。阅读例子让我感到困惑。这是我当前的代码,它显然不起作用。
TreeMap<LocalDate, BigDecimal> values = values.entrySet()
.stream()
.filter(p -> frequency.getEnd(p.getKey()) == p.getKey())
.collect(Collectors.toMap(/* HUH? */));
回答by Tunaki
Consider your problem like this: you have a Stream of entry of a map, that is to say a Stream<Map.Entry<LocalDate, BigDecimal>>
, and you want to collect it into a TreeMap<LocalDate, BigDecimal>
.
像这样考虑您的问题:您有一个地图条目的 Stream,即 a Stream<Map.Entry<LocalDate, BigDecimal>>
,并且您想将其收集到 a 中TreeMap<LocalDate, BigDecimal>
。
So, you are right, you should use Collectors.toMap
. Now, as you can see in the documentation, there are actually 3 Collectors.toMap
, depending on the arguments:
所以,你是对的,你应该使用Collectors.toMap
. 现在,正如您在文档中看到的,实际上有 3 个Collectors.toMap
,具体取决于参数:
toMap(keyMapper, valueMapper)
.keyMapper
is a function whose input is the stream current element and whose output is the key of the final Map. Thus, it maps the Stream element to a key (hence the name).valueMapper
is a function whose input is the stream current element and whose output is the value of the final Map.toMap(keyMapper, valueMapper, mergeFunction)
. The first two parameters are the same as before. The third,mergeFunction
, is a function that is called in case of duplicates key elements in the final Map; therefore, its input are 2 values (i.e. the two values for whichkeyMapper
returned the same key) and merges those two values into a single one.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)
. The first three arguments are the same as before. The fourth is a supplier of a Map: as it's currently implemented in the JDK, the two precedingtoMap
return aHashMap
instance. But if you want a specific Map instance, this supplier will return that instance.
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 实例,这个供应商将返回那个实例。
In our specific case, we need to use the third toMap
, because we want the result Map to explicitly be a TreeMap
. Let's see what input we should give it:
在我们的特定情况下,我们需要使用第三个toMap
,因为我们希望结果 Map 显式为 a TreeMap
。让我们看看我们应该给它什么输入:
keyMapper
: so this should return the key of the final Map. Here, we are dealing with aStream<Map.Entry<LocalDate, BigDecimal>>
so each Stream element is of typeMap.Entry<LocalDate, BigDecimal>
. This function then takes aMap.Entry<LocalDate, BigDecimal> e
as input. Its output should be the key of the final Map, in this case, the output should bee.getKey()
, i.e. theLocalDate
that the entry is holding. This can be written as a lambda expression:e -> e.getKey()
. This could also be written as a method-referenceMap.Entry::getKey
but let's stick with lambdas here, because it might be easier to understand.valueMapper
: this is the same as above, but, in this case, this function needs to returne.getValue()
, i.e. theBigDecimal
that the entry is holding. So this ise -> e.getValue()
.mergeFunction
: this is a tricky one. We know that there are no duplicate key elements (i.e. no duplicateLocalDate
) in the final Map, by construction. What do we write here? A simple solution is to throw an exception: this should not happen and if it does, there's a big problem somewhere. So whatever the two input arguments, we'll throw an exception. This can be written as(v1, v2) -> { throw new SomeException(); }
. Note that it needs to be enclosed in brackets. In this case, and to be consistent with what the JDK currently does, I've chosenSomeException
to beIllegalStateException
.mapSupplier
: as said before, we want to supply aTreeMap
. A supplier takes no argument and returns a new instance. So this can be written as() -> new TreeMap<>()
here. Again, we could use a method-reference and writeTreeMap::new
.
keyMapper
: 所以这应该返回最终地图的键。在这里,我们正在处理 ,Stream<Map.Entry<LocalDate, BigDecimal>>
因此每个 Stream 元素都是类型Map.Entry<LocalDate, BigDecimal>
。这个函数然后将 aMap.Entry<LocalDate, BigDecimal> e
作为输入。它的输出应该是最终 Map 的键,在这种情况下,输出应该是e.getKey()
,即LocalDate
条目所持有的 。这可以写成lambda表达式:e -> e.getKey()
。这也可以写成方法参考,Map.Entry::getKey
但让我们在这里坚持使用 lambda,因为它可能更容易理解。valueMapper
: 这和上面的一样,但是,在这种情况下,这个函数需要返回e.getValue()
,即BigDecimal
条目所持有的 。所以这是e -> e.getValue()
.mergeFunction
: 这是一个棘手的问题。LocalDate
通过构造,我们知道最终 Map中没有重复的关键元素(即没有重复)。我们在这里写什么?一个简单的解决方案是抛出一个异常:这不应该发生,如果发生了,那就是某处存在大问题。所以无论两个输入参数如何,我们都会抛出异常。这可以写成(v1, v2) -> { throw new SomeException(); }
. 请注意,它需要用括号括起来。在这种情况下,为了与 JDK 当前的功能保持一致,我选择SomeException
了IllegalStateException
.mapSupplier
: 如前所述,我们想提供一个TreeMap
. 供应商不接受任何参数并返回一个新实例。所以这可以写成() -> new TreeMap<>()
这里。同样,我们可以使用方法引用并写入TreeMap::new
.
Final code, where I've just written the collecting part of the Stream (note that in this code, you could also use the corresponding method-references, as said above, I added them here in comments):
最终代码,我刚刚编写了 Stream 的收集部分(请注意,在此代码中,您还可以使用相应的方法引用,如上所述,我在此处添加了它们的注释):
Collectors.toMap(
e -> e.getKey(), // Map.Entry::getKey
e -> e.getValue(), // Map.Entry::getValue
(v1, v2) -> { throw new IllegalStateException(); },
() -> new TreeMap<>()) // TreeMap::new
)
回答by Tagir Valeev
In addition to the previous answers note that if you don't need to keep the original map, you can perform such filtering in-place without using the Stream API:
除了前面的答案,请注意,如果您不需要保留原始地图,则可以在不使用 Stream API 的情况下就地执行此类过滤:
values.keySet().removeIf(k -> !frequency.getEnd(k).equals(k));
回答by Andreas
Since you're iterating Map.Entry
values, and toMap()
just needs two methods for extracting the key and the value, it's this simple:
由于您正在迭代Map.Entry
值,并且toMap()
只需要两种方法来提取键和值,就这么简单:
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
Note that this will notreturn a TreeMap
. For that, you need:
请注意,这不会返回TreeMap
. 为此,您需要:
Collectors.toMap(Entry::getKey,
Entry::getValue,
(v1,v2) -> { throw new IllegalStateException("Duplicate key"); },
TreeMap::new)
回答by Tom
The toMap method in its simplest form takes two arguments: one is a function to map the input to the key, and the other a function to map the input to the value. The output of both functions is combined to form an entry in the resulting map.
最简单形式的 toMap 方法有两个参数:一个是将输入映射到键的函数,另一个是将输入映射到值的函数。两个函数的输出组合在一起形成结果映射中的一个条目。
I think you need to do something like this:
我认为你需要做这样的事情:
Collectors.toMap(p -> p.getKey(), p -> p.getValue())