12

I have a map with duplicate values:

("A", "1");
("B", "2");
("C", "2");
("D", "3");
("E", "3");

I would like to the map to have

("A", "1");
("B", "2");
("D", "3");

Do you know how to get rid of the duplicate values?

At present, I get 'java.util.ConcurrentModificationException' error.

Thank you.

public static void main(String[] args) {

    HashMap<String, String> map = new HashMap<String, String>();
    map.put("A", "1");
    map.put("B", "2");
    map.put("C", "2");
    map.put("D", "3");
    map.put("E", "3");

    Set<String> keys = map.keySet(); // The set of keys in the map.

    Iterator<String> keyIter = keys.iterator();

    while (keyIter.hasNext()) {
        String key = keyIter.next();
        String value = map.get(key);

        System.out.println(key + "\t" + value);

        String nextValue = map.get(key);

        if (value.equals(nextValue)) {
            map.remove(key);
        }
    }
    System.out.println(map);
}
3
  • 4
    Why do you keep B and leave out C? Remember, HashMap doesn't maintain the order of elements inserted. Commented Jul 23, 2013 at 14:03
  • Can you tell the exact requirement? Looks like you want unique values. HashMap can give you unique keys. Will inverting the key and value be enough for you? Commented Jul 23, 2013 at 14:05
  • @RohitJain +1 That's the question ! Commented Jul 23, 2013 at 14:15

11 Answers 11

9

Assuming that you use Java 8, it could be done using the Stream API with a Set<String> that will store the existing values:

Map<String, String> map = new HashMap<>();
map.put("A", "1");
...
System.out.printf("Before: %s%n", map);

// Set in which we keep the existing values
Set<String> existing = new HashSet<>();
map = map.entrySet()
    .stream()
    .filter(entry -> existing.add(entry.getValue()))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
System.out.printf("After: %s%n", map);     

Output:

Before: {A=1, B=2, C=2, D=3, E=3}
After: {A=1, B=2, D=3}

NB: Strictly speaking a predicate of a filter is not supposed to be stateful, it should be stateless as mentioned into the javadoc in order to ensure that the result remain deterministic and correct even if we use a parallel stream. However here, I assume that you don't intend to use a parallel stream such that this approach remains valid.

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

3 Comments

Strictly speaking streams shouldn't really be used with a side-effecting filter.
@GraemeMoss that's right, I added a comment to avoid misuse/misunderstanding
You can replace .filter() by .distinct()
9

make a reverse HashMap!

HashMap<String, String> map = new HashMap<String, String>();
Set<String> keys = map.keySet(); // The set of keys in the map.

Iterator<String> keyIter = keys.iterator();

while (keyIter.hasNext()) {
    String key = keyIter.next();
    String value = map.get(key);
    map.put(value, key);
}

now that you have the hashMap you need reverse it or print it.

in anyway do not delete while iterating hashMap. save the values in a list and delete them in an outer loop

5 Comments

It will still be random which element will be kept (because the order of a HashMap is undefined), but if that's no issue, this works fine.
@Heuster i agree, but he didn't said it's an issue
@NoIdeaForName why there is map.add() and not map.put()
@bot13 can't say i remember if there was a reason for this, it was 6 years back. works with put as well.
It is only possible if both key and value are of same type
5
    Map<String,Object> mapValues = new HashMap<String,Object>(5);
    mapValues.put("1", "TJ");
    mapValues.put("2", "Arun");
    mapValues.put("3", "TJ");
    mapValues.put("4", "Venkat");
    mapValues.put("5", "Arun");

    Collection<Object> list = mapValues.values();
    for(Iterator<Object> itr = list.iterator(); itr.hasNext();)
    {
        if(Collections.frequency(list, itr.next())>1)
        {
            itr.remove();
        }
    }

1 Comment

Some explanation or links for further details would be helpful. Especially if asked why some Exception thrown there is need.
3

This can be done using Java 8. The concept of stream is required. The pseudocode, is stream().filter().collect(). If the Initial Map : {A=1, B=2, C=2, D=3, E=3}. Then the required answer after removing the duplicates is {A=1, B=2, D=3} .

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class RemoveDuplicates1 {
   public static void main(String[] args) {

        //Initial Map : {A=1, B=2, C=2, D=3, E=3}
        //After =>  {A=1, B=2, D=3} 

      Map<String , String > map = new HashMap<>();
        map.put("A", "1");
        map.put("B", "2");
        map.put("C", "2");
        map.put("D", "3");
        map.put("E", "3");

        System.out.printf("before :   " +map );
        System.out.println("\n");

        Set<String> set = new  HashSet<>();

        map = map.entrySet().stream()
                .filter(entry -> set.add(entry.getValue()))
                .collect(Collectors.toMap(Map.Entry :: getKey ,  Map.Entry :: getValue));
        System.out.printf("after => " + map);

   }
}

Comments

2

ConcurrentModificationException happening,because you are removing from map

  if (value.equals(nextValue)) {
            map.remove(key);
        }

You have to remove from iterator

if (value.equals(nextValue)) {
            keyIter.remove(key);
        }

Coming to the duplicate entry issue,Its pretty simple :Find duplicate values in Java Map?

7 Comments

Well, if you see his code clearly, this won't really solve his problem.
This code is wrong , it won't compile and neither does it solves the problem .
iterator ,Iterator<String> keyIter = keys.iterator();
And what is keyIter.remove(key) ?
@TheNewIdiot :That avoids the exception The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method.
|
1

If this is yours frequent requirement then DualHashBidiMap calss of apache's commons.collections will help you more instead of using HashMap.

Comments

1
public static void main(String[] args) {
    Map<String, String> map = new HashMap<>();
    map.put("A", "1");
    map.put("B", "2");
    map.put("C", "2");
    map.put("D", "3");
    map.put("E", "3");
    System.out.println("Initial Map : " + map);
    for (String s : new ConcurrentHashMap<>(map).keySet()) {
        String value = map.get(s);
        for (Map.Entry<String, String> ss : new ConcurrentHashMap<>(map)
                .entrySet()) {
            if (s != ss.getKey() && value == ss.getValue()) {
                map.remove(ss.getKey());
            }
        }
    }
    System.out.println("Final Map : " + map);
}

Comments

0

This can be easily done by putting your hashmap into arraylist. This arraylist is of hashmap type.

ArrayList<HashMap<String, String>> mArrayList=new ArrayList<>();
HashMap<String, String> map=new HashMap<>();
map.put("1", "1");
        mArrayList.add(map);
        map=new HashMap<>();
        map.put("1", "1"); 
        mArrayList.add(map);
        map=new HashMap<>();
        map.put("1", "2");
        mArrayList.add(map);
        map=new HashMap<>();
        map.put("1", "3");
        mArrayList.add(map);
        map=new HashMap<>();
        map.put("1", "2");
        mArrayList.add(map);

for(int i=0;i<mArrayList.size();i++)
        {
            temp=mArrayList.get(i).get("1");
            for(int k=i+1;k<mArrayList.size();k++)
            {
                if(temp.equals(mArrayList.get(k).get("1")))
                {
                    mArrayList.remove(k); 
                } 
            }

        }

Now print your arraylist...all the duplicate values from the hashmap easily removed...This is the easiest way to remove duplicacy

Comments

0

This will be helpful to remove duplicate values from map.

    Map<String, String> myMap = new TreeMap<String, String>();
    myMap.put("1", "One");
    myMap.put("2", "Two");
    myMap.put("3", "One");
    myMap.put("4", "Three");
    myMap.put("5", "Two");
    myMap.put("6", "Three");

    Set<String> mySet = new HashSet<String>();

    for (Iterator itr = myMap.entrySet().iterator(); itr.hasNext();)
    {
        Map.Entry<String, String> entrySet = (Map.Entry) itr.next();

        String value = entrySet.getValue();

        if (!mySet.add(value))
        {
            itr.remove();               
        }
    }

System.out.println("mymap :" + mymap);

Output:

mymap :{1=One, 2=Two, 4=Three}

Comments

0

If you are looking just to remove the concurrentModification exception, then just replace your HashMap with ConcurrentHashMap.

To know more about ConcurrentHashMap look here

Comments

0

It can be done without mutating the original map:

public static <K,V> Map<K,V> getDistinctValues(Map<K,V> map) {
    Set<V> values = new HashSet<>();

    return map.entrySet().stream()
            .filter(entry -> values.add(entry.getValue()))
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    Map.Entry::getValue
            ));
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.