59

I have a HashMap which I need to filter using some function:

HashMap<Set<Integer>, Double> container
Map.Entry<Set<Integer>, Double> map = container.entrySet()
            .stream()
            .filter(k -> k.getKey().size() == size)

For the size = 2 the following should be valid:

containerBeforeFilter = {1,2,3} -> 1.5, {1,2} -> 1.3
containerAfterFilter = {1,2} -> 1.3

After I applied the function in the filter, I want to collect results again into a HashMap. However, when I try to apply the method suggested here, I'm getting illegal statements.

So the following statement, applied after the filter, is illegal:

.collect(Collectors.toMap((entry) -> entry.getKey(), (entry) -> entry.getValue()));

What would be the proper way of collecting unchanged map values, where the only criteria is satisfying some key?

UPDATE

The mistake in the above code is the declared type of the variable map. It should have been Map rather than Map.Entry.

So the now functional code is:

Map<Set<Integer>, Double> map = container.entrySet()
            .stream()
            .filter(k -> k.getKey().size() == size)
            .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
6
  • Can you post which Java version you're using? I can't reproduce your problem. Commented Nov 9, 2015 at 9:48
  • It is the latest JDK 8u65 Commented Nov 9, 2015 at 9:51
  • 1
    I'm also using latest JDK and this doesn't work for you: Map<Set<Integer>, Double> collect = container.entrySet().stream().filter(k -> k.getKey().size() == size).collect(Collectors.toMap(Entry::getKey, Entry::getValue)); Commented Nov 9, 2015 at 9:52
  • 1
    Ok, I found the problem thanks to you @Flown! The problem was the type of the map variable! It must be Map not Map.Entry. Very silly mistake. Now the Collectors work as well! Thanks! Commented Nov 9, 2015 at 9:54
  • @wero Possibly. NetBeans 8.0.2 doesn't complain and it builds successfully. Commented Nov 9, 2015 at 10:03

2 Answers 2

58

If your ending map has a chance of "duplicate keys", there is a better solution using

toMap(Function<? super T, ? extends K> keyMapper,
                            Function<? super T, ? extends U> valueMapper,
                            BinaryOperator<U> mergeFunction,
                            Supplier<M> mapSupplier)

You could use it like this:

HashMap<Set<Integer>, Double> map = container.entrySet()
    .stream()
    .filter(k -> k.getKey().size() == size)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (prev, next) -> next, HashMap::new));

Then as duplicate keys are added, it will use the latest instead of throwing an exception. The last parameter is optional.

If you want to keep duplicate keys into a list, then use Collectors.groupingBy instead.

Sign up to request clarification or add additional context in comments.

4 Comments

thank you , can you please explain , what are you doing here ,.... Map.Entry::getKey, Map.Entry::getValue, (prev, next) -> next, HashMap::new) ??
@Shyam, you can find explanation at Javadocs: Map.Entry::getKey equals to entry -> entry.getKey() - keyMapper mapping function to produce keys Map.Entry::getValue equals to entry -> entry.getValue() - valueMapper mapping function to produce values (prev, next) -> next - mergeFunction merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction) HashMap::new equals to () -> new HashMap<>() - mapSupplier function which returns a new, empty Map into which the results will be inserted
thank you so much , can you elaborate a bit on """ (prev, next) -> next - mergeFunction merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction) """ where are you mentioning here to merge ?
here """ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (prev, next) -> next, HashMap::new)); """ collect returns HashMap ? HashMap takes (K &V) right , why are passing 4 arguments here ... how it works ? what is key here and what is val for returning hashmap ?
36

Seems that Collectors.toMap does not pick up the type arguments of stream.collect in your example and only returns a Map<Object,Object>.

As a workaround you can create the result map yourself and in the last stream step add the filtered entries to the result map:

Map<Set<Integer>, Double> result = new HashMap<>();
container.entrySet()
    .stream()
    .filter(entry -> entry.getKey().size() == size)
    .forEach(entry -> result.put(entry.getKey(), entry.getValue()));

3 Comments

Yes, this approach works well! Although, it is weird why the other approach fails. Thanks!
Collectors.toMap has bug. Its not accepting duplicate values
If you have a chance of duplicate keys, see the other answer...in this case you don't because you know the input started as a Hash...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.