Problem with Maps in java
I have a Hashmap which has X number of elements
I need to move this map into another map This is what my code looks likeMap originMap = initialize();
Map destMap = new Hashmap ();
int originMapSize = originMap.size();
Set<Map.Entry<K, V>> entries = originMap.entrySet();
for (Map.Entry<K, Y> mapEntry : entries) {
K key = mapEntry.getKey();
V value = mapEntry.getValue();
destMap.put (key,value);
}
// Shouldnt this be equal to originMapSize ????
int destMapSize = destMap.size();
What I am observing is - originMapSize is NOT equal to the destMapSize
It seems when we put the elements in the destMap, some of the elements are being overridden
We have overrridden the hashCode and equals method- and it is a suspicious impleme开发者_如何学JAVAntation.
However, if the originMap allowed the elements to be added, why would the destinationMap not add a new elements and override an existing element instead ?This could happen if the equals
method was asymmetric. Suppose there are two keys a and b such that:
a.hashCode() == b.hashCode()
a.equals(b)
returns falseb.equals(a)
returns true
Then suppose that the HashMap
implementation searches for an existing key by calling existingKey.equals(newKey)
for each existing key with the same hash code as the new key.
Now suppose we originally add them in the order { a, b }.
The first key (a
) obviously goes in with no problems. The second key (b
) insertion ends up calling a.equals(b)
- which is false, so we get two keys.
Now building the second HashMap
, we may end up getting the entries in the order { b, a }.
This time we add b
first, which is fine... but when we insert the second key (a
) we end up calling b.equals(a)
, which returns true, so we overwrite the entry.
That may not be what's going on, but it could explain things - and shows the dangers of an asymmetric equals
method.
EDIT: Here's a short but complete program demonstrating this situation. (The exact details of a
and b
may not be the same, but the asymmetry is.)
import java.util.*;
public class Test {
private final String name;
public Test(String name)
{
this.name = name;
}
public static void main(String[] args)
{
Map<Test, String> firstMap = new HashMap<Test, String>();
Test a = new Test("a");
Test b = new Test("b");
firstMap.put(b, "b");
firstMap.put(a, "a");
Map<Test, String> secondMap = new HashMap<Test, String>();
for (Map.Entry<Test, String> entry : firstMap.entrySet())
{
System.out.println("Adding " + entry.getKey().name);
secondMap.put(entry.getKey(), entry.getValue());
}
System.out.println(secondMap.size());
}
@Override public int hashCode()
{
return 0;
}
@Override public boolean equals(Object other)
{
return this.name.equals("b");
}
}
Output on my machine:
Adding a
Adding b
1
You may not get the output that way round - it depends on:
- The way that
equals
is called (candidateKey.equals(newKey)
or vice versa) - The order in which entries are returned from the set
It may even work differently on different runs.
Those values should be equal, but the problem is you are iterating over a different Map object.
for (Map.Entry mapEntry : entries)
is not the same as
for (Map.Entry mapEntry : originMap)
I suspect the order of the elements being added to the first hashmap is not the same as the order added to the second. This combined with the sketchy hashCode method is causing duplicates to be added to the first.
Try changing hashCode to always return the same value to see if your problem goes away.
Why don't you use destMap.putAll(originMap) ?
Map has a putAll method. Try something like this:
Map<String, String> destination = new HashMap<String, String>();
Map<String, String> original = new HashMap<String, String>();
destination.putAll(original);
It depends of how the first HashMap is initialized. Also everytime you add an object into the HashMap , once it passes 75% load factor, it allocates twice the default size to accomodate new values. Maps usually have default size = 16: when you pass the 75% load factor it enlarges to 32.
精彩评论