49

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()));
rogerdpack
  • 62,887
  • 36
  • 269
  • 388
chao
  • 1,893
  • 2
  • 23
  • 27
  • Can you post which Java version you're using? I can't reproduce your problem. – Flown Nov 09 '15 at 09:48
  • It is the latest JDK 8u65 – chao Nov 09 '15 at 09:51
  • 1
    I'm also using latest JDK and this doesn't work for you: `Map, Double> collect = container.entrySet().stream().filter(k -> k.getKey().size() == size).collect(Collectors.toMap(Entry::getKey, Entry::getValue));` – Flown Nov 09 '15 at 09: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! – chao Nov 09 '15 at 09:54
  • @wero Possibly. NetBeans 8.0.2 doesn't complain and it builds successfully. – chao Nov 09 '15 at 10:03
  • You might simplify by using `container.keySet()` – Manos Nikolaidis Nov 09 '15 at 10:10

2 Answers2

50

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.

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
provisota
  • 1,390
  • 1
  • 14
  • 14
  • 1
    thank you , can you please explain , what are you doing here ,.... Map.Entry::getKey, Map.Entry::getValue, (prev, next) -> next, HashMap::new) ?? – BdEngineer Jun 18 '19 at 18:50
  • 2
    @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 – provisota Jun 19 '19 at 21:48
  • 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 ? – BdEngineer Jun 20 '19 at 13:06
  • 1
    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 ? – BdEngineer Jun 20 '19 at 13:09
29

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()));
wero
  • 32,544
  • 3
  • 59
  • 84