0

I have a HashMap and I need to filter that map or create a map only with keys containing duplicate values in the given map. Please suggest how can we achieve this?

Pls note given input is only map. And no other value to search.

      Map map = new HashMap();
         map.put(1, "abc");
         map.put(2, "def");
         map.put(3, "mno");
         map.put(4, "abc");
         map.put(5, "ijk");
         map.put(6, "abc");

      Map result = new HashMap();
          (1, "abc")
          (4, "abc")
          (6, "abc");

I expect a new map containing all keys with dupliacte values

2
  • 2
    What would happen if there were more than one duplicated value? For example, "def" is present twice. Commented Jun 19, 2019 at 15:19
  • Hi Andy, I would need all the keys with duplicate values in the new Map, even if there is more than one Commented Jun 19, 2019 at 15:47

3 Answers 3

2

You can do this using streams.

Firstly, group the entries together so entries with the same value are together:

Map<String, Map.Entry<Integer, String>> grouped =
    map.entrySet().stream()
       .collect(groupingBy(Map.Entry::getValue));

Then remove entries where there was only 1 occurrence of the value:

grouped.values().removeIf(m -> m.size() <= 1);

Then flatten the entries back out again:

Map<Integer, String> result = grouped.values().stream()
    .flatMap(m -> m.entrySet().stream())
    .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));

You can do this in a single expression too:

Map<Integer, String> grouped =
    map.entrySet().stream()
        .collect(groupingBy(Map.Entry::getValue))
        .values().stream()
        .filter(m -> m.size() > 1)
        .map(Map::entrySet)
        .flatMap(Collection::stream)
        .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
Sign up to request clarification or add additional context in comments.

Comments

1

You can create a map from the values to their frequency

Map<V, Long> counts = map.values().stream()
    .collect(Collectors.groupingBy(
        Function.identity(), Collectors.counting()));

And then filter the stream of entries of the original map, retaining the elements that have a count larger than 1, and create a new map from that:

Map<K, V> result = map.entrySet().stream()
    .filter(e -> counts.get(e.getValue()) > 1)
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

Here is an example:

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;

public class RetainFrequentValues
{
    public static void main(String[] args)
    {
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "abc");
        map.put(2, "def");
        map.put(3, "mno");
        map.put(4, "abc");
        map.put(5, "ijk");
        map.put(6, "abc");

        // For testing
        map.put(7, "def");

        Map<Integer, String> result = retainFrequentValues(map);
        result.entrySet().forEach(System.out::println);
    }

    private static <K, V> Map<K, V> retainFrequentValues(Map<K, V> map)
    {
        Map<V, Long> counts = map.values().stream()
            .collect(Collectors.groupingBy(
                Function.identity(), Collectors.counting()));
        Map<K, V> result = map.entrySet().stream()
            .filter(e -> counts.get(e.getValue()) > 1)
            .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
        return result;
    }

}

2 Comments

Would this work for a Hashmap with String values? Would only need to change the Long for a String?
The method is independent of the key- and value type.
0

Leveraging Google's Guava collection:

Import:

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Maps;

Usage:

Map<Integer, String> map = new HashMap<>();
map.put(1, "abc");
map.put(2, "def");
map.put(3, "mno");
map.put(4, "abc");
map.put(5, "ijk");
map.put(6, "abc");

HashMultiset<String> ms = HashMultiset.create();
ms.addAll(map.values());
ms.removeIf(e -> ms.count(e) == 1);

Map<Integer, String> result = Maps.filterValues(map, ms::contains);

The result: {1=abc, 4=abc, 6=abc}

If Guava is not an option, please see one of the answers above.

Comments

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.