How do I handle duplicate keys in HashMaps in Java?
To achieve what you actually ask for:
Before your put
line:
while (data.containsKey(key)) key += "_";
data.put(key, value);
This will keep on checking the map to see if key
already exists, and if it does, it adds the _
to the end, and tries again.
You can do these two lines in one:
while (data.putIfAbsent(key, value) != null) key += "_";
This does basically the same, but it just avoids having to look up twice in the case that the key isn't found (and thus the value should be inserted).
However, consider whether this is actually the best thing to do: how will you then look up things by "key", if you've essentially made up the keys while reading them.
You can keep multiple values per key by using a value type which stores multiple values, e.g. List<String>
.
HashMap<String, List<String>> data = new HashMap<>();
// ...
data.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
Why Hashmap values are getting replaced when duplicate key are used, even though it uses linked list for each bucket internally?
You inserted the values 1, 2, 3
into key 1
, and the values 1, 2
into key 2
. Each time you insert a value into a key, you overwrite the value which previously existed at the key, assuming there were a previous value. So, your code is functionally identical to this:
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(1, 3);
map.put(2, 2);
That is, only the latest key-value assignments actually "stick."
Map implementation with duplicate keys
You are searching for a multimap, and indeed both commons-collections and Guava have several implementations for that. Multimaps allow for multiple keys by maintaining a collection of values per key, i.e. you can put a single object into the map, but you retrieve a collection.
If you can use Java 5, I would prefer Guava's Multimap
as it is generics-aware.
HashMap holding duplicate keys
HashMap
is not thread safe, as explicitly noted in the linked docs. You are providing a good example of why this is so. Yes, you are putting in duplicate keys because put
does not check that another thread is putting the same key in. That is what it means not to be thread safe.
The retrieval behavior is undefined, so it can return whichever value it wants at that point. It is probably very implementation, platform and even timing-dependent.
There are a couple of workarounds. The one suggested in the docs is
Map m = Collections.synchronizedMap(new HashMap(...));
Another option is to use ConcurrentHashMap
, which is explicitly designed for the purpose.
HashMap creating duplicate keys?
HashMap
uses hashing function to calculate indices for objects used as key
. For this functionality to work properly you need to provide both equals()
AND hashCode()
functions for class that instances of you plan to use as keys in your hash map.
That means you need to override public int hashCode()
method that's now inherited straight from Object
superclass, and put there some calculations that will return the same integer for objects that are considered 'equal'. Fortunately you compare String
s as this criteria so you can use String
method hashCode
resulting with:
@Override
public int hashCode() {
return s.hashCode();
}
Second problem that I previously missed is your implementation of equals()
is wrong. You should override
Object
's equals()
which has a signature public boolean equals( Object o )
and you implemented it with Vertex
as an argument, which caused that while adding to hashmap Object
's equals was used, as you didn't override by overload
equals. That's one of the reasons that annotation
@Overrideshould be used, compiler would tell you that you don't override anything and you would know that you're doing something wrong. Now, going back to how to implement
equals` properly....
@Override
public boolean equals(Object o) {
if( o instanceof Vertex ) {
return s.equals((( Vertex)o).getName());
} else {
return false;
}
}
Now instances of Vertex
will behave properly when used as key in hash map.
As a side note, putting all the logic, including opening, parsing file in constructor is a little too much to my taste, consider splitting it into some methods instead.
Java HashMap when duplicate keys are inserted
Your output is printing the keys, not the values. In your code the key will not change but the value will.
For instance, if you change your output loop to:
for (Emp emp : emp.values()) {
System.out.println("Name: " + e.name + " , age: " + e.age);
}
I suspect you'll see the answer you were expecting.
However: In general I would advise against doing what you're doing here. All kinds of code expects that if a.equals(b)
then a
and b
don't have any meaningful differences at all, and your Emp
class's equals
implementation doesn't live up to that contract. For instance, if you used a HashSet
rather than a HashMap
you would get much more peculiar behavior and there'd be no way to fix it.
A better way to implement this might be to have Emp
's hashCode
and equals
methods properly respect name and age, and make your map be a Map<String, Emp>
where the key is the employee's name and the value is the Emp
record.
Related Topics
Com.Jcraft.Jsch.Jschexception: Unknownhostkey
Use Cases and Examples of Gof Decorator Pattern for Io
How to Set Specific Java Version to Maven
Green Threads VS Non Green Threads
What Is Recommended Way for Spawning Threads from a Servlet in Tomcat
How to Remove the Last Character from a String
How to Re-Attach Detached Objects in Hibernate
Make Jackson Interpret Single JSON Object as Array with One Element
Create Java Console Inside a Gui Panel
How to Convert Java String to Date Object
How to Access Owl Documents Using Xpath in Java
Java: Detect Duplicates in Arraylist
What Is the Use of Marker Interfaces in Java
Reversing a Linked List in Java, Recursively
How to Make Threadpoolexecutor's Submit() Method Block If It Is Saturated
Why Does Java Have an "Unreachable Statement" Compiler Error