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.
.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.
TreeMap<LocalDate, BigDecimal> values = values.entrySet()
.stream()
.filter(p -> frequency.getEnd(p.getKey()) == p.getKey())
.collect(Collectors.toMap(/* HUH? */));
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:
values.keySet().removeIf(k -> !frequency.getEnd(k).equals(k));
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>.
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:
toMap(keyMapper, valueMapper).keyMapperis 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).valueMapperis 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 whichkeyMapperreturned 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 precedingtoMapreturn aHashMapinstance. But if you want a specific Map instance, this supplier will return that instance.
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:
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> eas input. Its output should be the key of the final Map, in this case, the output should bee.getKey(), i.e. theLocalDatethat 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::getKeybut 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. theBigDecimalthat 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 chosenSomeExceptionto 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.
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):
Collectors.toMap(
e -> e.getKey(), // Map.Entry::getKey
e -> e.getValue(), // Map.Entry::getValue
(v1, v2) -> { throw new IllegalStateException(); },
() -> new TreeMap<>()) // TreeMap::new
)
Since you're iterating Map.Entry values, and toMap() just needs two methods for extracting the key and the value, it's this simple:
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
Note that this will not return a TreeMap. For that, you need:
Collectors.toMap(Entry::getKey,
Entry::getValue,
(v1,v2) -> { throw new IllegalStateException("Duplicate key"); },
TreeMap::new)
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.
I think you need to do something like this:
Collectors.toMap(p -> p.getKey(), p -> p.getValue())
来源:https://stackoverflow.com/questions/33724733/confused-by-java8-collectors-tomap