adding the same key twice in the Map - java

I was doing research on Maps and I discovered that if I add the same key twice deliberately then the size of the Map remains the same. What's the technical reason behind this?
Map map=new HashMap();//HashMap key random order.
map.put("Amit","Java");
map.put("Amit","Java");
Code for retrieving...
System.out.println("There are "+map.size()+" elements in the map.");
System.out.println("Content of Map are...");
Set s=map.entrySet();
Iterator itr=s.iterator();
while(itr.hasNext())
{
Map.Entry m=(Map.Entry)itr.next();
System.out.println(m.getKey()+"\t"+m.getValue()+"\t"+ m.hashCode());
}
The result that I get:
There are 1 elements in the map.
Content of Map are...
Amit Java 3943477

Because Map's contract is that keys must be unique. So if you associate a new value to an existing key, it will override the value of the existing entry, not create a new entry:
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
You can also check Map#put() javadoc (emphasis mine):
Associates the specified value with the specified key in this map (optional operation). If the map previously contained a mapping for the key, the old value is replaced by the specified value. (A map m is said to contain a mapping for a key k if and only if m.containsKey(k) would return true.)

A standard Java Map can only have one value per key. Note that that value could be a collection, and thus you can effectively store multiple values per key.
If you want multiple identical keys in a map, various solutions exist. See the Guava Multimap, for example.

If the new key is same as any of the existing keys, then the value in the map is overwritten.

Related

Having HashMap inside a HashMap - call by reference or duplicate keys?

I was just wondering, when having a HashMap<HashMap<Integer, Integer>, String> and I add as key a new HashMap, does it get treated as a duplicate key or we have a call by reference and the value is not looked at at all?
Thanks :)
#Stephen has given a very good conceptual explanation.
So Just in brief:
When we are "putting" something in a HashMap it is internally stored in a "table" which is an array of "Entry"
Now at which position (bucketIndex) of that table it will be stored will be decided based on the "hashcode" of the key to be stored.Before storing it JVM will check if in that bucketIndex any "Entry" is present.This case is possible when two "keys" would have same hashcode.Now if there is an Entry JVM will further check if the "key" itself is identical(to know identical JVM will call "equals" on that key) to "key" already present.If yes it will consider it as a duplicate key and update its respective value in "Entry". If no it will just add another entry at the same bucket Index.
At time of "getting" the value from the map by sending a "key" ,similar process will run.First "hashcode" of the key would be extracted and depending on this the position (bucketIndex) of the table to be looked would be determined. Now if there is no content in that index "null" would be returned.Otherwise...
JVM will go to that index and there is a possibility that more than one "Entry" can be there because more than one object can have same hashcode. Now JVM will call "equals" method on that key to check if the Key present in the table is same as the inputKey sent for retrieval of value. if "equals" returns true then we will get the desired value.
So in general a key will be considered as a duplicate key if and only if hashcode() for both will return same value and equals() will return true.
Now coming to your question "I add as key a new HashMap, does it get treated as a duplicate key ", the answer is yes if and only if your new HashMap has exact same Entry i.e key,value pairs as that of an existing HashMap key.
Because :
if you will have a look at hashcode() implemenatation of HashMap then you will see its hashcode is calculated based on the "Key" and "value".So if 2 hashmaps will have same set,they will have the same hashcode().
the equals() of HashMap checks if Entry are identical.So again if two Hashmaps will have same set they are equal.
Now see the below code demonstrating this concept :
public static void main(String[] args) {
// TODO Auto-generated method stub
HashMap<Integer,Integer> keyMap=new HashMap<Integer, Integer>();
keyMap.put(2, 1020);
keyMap.put(3, 1352);
keyMap.put(23,1256);
System.out.println("hashcode keymap1:"+keyMap.hashCode());
HashMap<Integer,Integer> keyMap2=new HashMap<Integer, Integer>();
keyMap2.put(1, 100);
keyMap2.put(4, 152);
keyMap2.put(43,156);
System.out.println("hashcode keymap2:"+keyMap2.hashCode());
HashMap<HashMap<Integer,Integer>,String> mainMap=new HashMap<HashMap<Integer,Integer>,String>();
mainMap.put(keyMap, "1st value");
mainMap.put(keyMap2, "2ndvalue");
System.out.println(mainMap);
HashMap<Integer,Integer> keyMap3=new HashMap<Integer, Integer>();
keyMap3.put(23,1256);
keyMap3.put(3, 1352);
keyMap3.put(2, 1020);
System.out.println("hashcode keymap3:"+keyMap3.hashCode());
mainMap.put(keyMap3, "3rd value");
System.out.println(mainMap);
if(mainMap.containsKey(keyMap3))
System.out.println(" value retrieved is :"+mainMap.get(keyMap3));
else
System.out.println("key not found");
}
Here you can observe keymap and keymap3 are having same set of key,value and same hashcode. So both are here duplicate key, hence value of keymap is updated by value of keymap3.
The contract for the HashMap.equals(Object) method is:
"Compares the specified object with this map for equality. Returns true if the given object is also a map and the two maps represent the same mappings. More formally, two maps m1 and m2 represent the same mappings if m1.entrySet().equals(m2.entrySet())."
Now the standard behavior of a Map is to treat keys as the same if equals(Object) says they are equal.
So the answer to your question is that if you have
HashMap<Integer, Integer> k1 = // some map
HashMap<Integer, Integer> k2 = // another map
HashMap<HashMap<Integer, Integer>, String> map = // some
then using k1 and k2 as keys in map would give you one entry if k1.equals(k2) and two entries otherwise.
And given that k1 and k2 are maps, we determine if they are equal by comparing their respective sets of map entries.
This has two obvious problems:
If you change k1 or k2 while they are keys for entries in map, then you break a fundamental invariant for map. When that happens you will find that operations on map give incorrect results; e.g. map.get(k1) will give the wrong answer.
Whenever you do an operation involving a lookup on map, you will call HashMap.hashCode() for a key object. Calculating the hash code for the key entails calculating the hashcode for each and every key and value in the map HashMap<Integer, Integer>. That is expensive, especially since this HashMap.hashCode() does not (cannot) cache anything.
In short, using a HashMap as a key for another HashMap is a bad idea.
So, to answer your question:
I was just wondering, when having a HashMap<HashMap<Integer, Integer>, String> and I add as key a new HashMap, does it get treated as a duplicate key or we have a call by referencea and the value is not looked at at all?
It will not be a duplicate keyb unless the respective keys are maps with the same set of entries. This is what HashMap.equals(Object) tests.
It is not compared as a reference; i.e. it is not compared with == semantics. The HashMap.equals(Object) method is used for comparisons.
a - Note that "call by reference" terminology is not applicable to this situation. Call by reference / call by value is about how parameters are passed when a method is called.
b - .... provided that you don't violate the invariant.

Unsupported add/addAll operations for Map<K,V>.keySet()

Regarding the Map<K,V> interface:
Why does keySet() return a Set that supports the remove operation but doesn't support add() and addAll() operations?
The Set returned by keySet is backed by the Map, so changes to the map are
reflected in the set, and vice-versa. This means that calling remove on that Set removes the matching Entry from the Map.
It would make no sense to call add or addAll on that Set, since you can't add key[s] without corresponding value[s] to the Map.
Think about what you are asking for:
you want to retrieve all KEYS of a map (and that set is not a "copy" of the keys; it represents the keys of the map).
And then you ask to add elements to those KEYS. In other words: the "data set" you are looking at has the semantic meaning of keys coming from a map. And you want to increase that "data set" - but without providing the corresponding entries for that map.
Deletion on the other hand is straight forward; deleting a key will also delete the corresponding entry from the map.
It's because each key in the set is linked to a value in the map. Removing a key will remove the associated value, but to add you'll need a value and not just a key.

Java - what is returned when two keys map to same value?

In Java, I understand if two keys maps to one value , linear chaining occurs due to collision.
For Example:
 Map myMap= new HashMap(); //Lets says both of them get mapped to same bucket-A and
myMap.put("John", "Sydney");//linear chaining has occured.
myMap.put("Mary","Mumbai"); //{key1=John}--->[val1=Sydney]--->[val2=Mumbai]
So when I do:
myMap.get("John"); // or myMap.get("Mary")
What does the JVM return since bucket-A contains two values?
Does it return the ref to "chain"? Does it return "Sydney"? Or does it return "Mumbai"?
Linear chaining happens when your keys have the same hashcode and not when two keys map to one value.
So when I do: myMap.get("John"); // or myMap.get("Mary")
map.get("John") gives you Sydney
map.get("Mary") gives you Mumbai
What does the JVM return since bucket-A contains two values?
If the same bucket contains two values, then the equals method of the key is used to determine the correct value to return.
It is worthwhile mentioning the worst-case scenario of storing (K,V) pairs all having the same hashCode for Key. Your hashmap degrades to a linked list in that scenario.
The hashCode of your method determines what 'bucket' (aka list, aka 'linear chain') it will be put in. The equals method determines which object will actually be picked from the 'bucket', in the case of collision. This is why its important to properly implement both methods on all object you intend to store in any kind of hash map.
Your keys are different.
First some terminology
key: the first parameter in the put
value: the second parameter in the put
entry: an Object that holds both the key & the value
When you put into a HashMap the map will call hashCode() on the key and work out which hash bucket the entry needs to go into. If there is something in this bucket already then a LinkedList is formed of entries in the bucket.
When you get from a HashMap the map will call hashCode() on the key and work out which hash bucket to get the entry from. If there is more than one entry in the bucket the the map will walk along the LinkedList until it finds an entry with a key that equals() the key supplied.
A map will always return the Object tied to that key, the value from the entry. Map performance degrades rapidly if hashCode() returns the same (or similar) values for different keys.
You need to use java generics, so your code should really read
Map<String, String> myMap = new HashMap<String, String>();
This will tell the map that you want it to store String keys and values.
From my understanding, the Map first resolves the correct bucket (identified by the hashcode of the key). If there's more than one key in the same bucket, the equals method is used to find the right value in the bucket.
Looking at your example what confuses you is that you think values are chained for a given key. In fact Map.Entry objects are chained for a given hashcode. The hashCode of the key gives you the bucked, then you look at the chained entries to find the one with the equal key.

How can I have a HashMap with unique keys in java?

How can I have a HashMap with unique keys in Java?
Or even does this make any sense to have unique keys in HashMap or the keys are unique by default?
I am a newbie.
thx
Hash map key is unique. Add duplicate key, then it will be overwritten.
HashMap hm = new HashMap();
hm.put("1", new Integer(1));
hm.put("2", new Integer(2));
hm.put("3", new Integer(3));
hm.put("4", new Integer(4));
hm.put("1", new Integer(5));// value integer 1 is overwritten by 5
By default Hashmap is not synchronized.
The keys are unique in all maps. The difference between the various maps implementations concern the possibility of null keys, the order of iteration and concurrency issues.
Try to look at the Java API for Map which is interface that HashMap implements. The first sentence is:
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
HasMap has unique keys. as .keySet() returns Set which has unique members
HashMap is a collection to store (key,value) pairs and According to the documentation of HashMap the keys are always unique.
If you add a key which already exists(collision) in the hashmap, the old value will be replaced.
A generic hashmap is usually implemented as an associative array, so let's say your array has N elements, from 0 to N-1, when you want to add a new (key, value) pair, what it's done behind the scenes is (just conceptually):
index = hash(key) mod N
array[index] = value
So, by construction, a key is mapped to one and one only array entry.
Please note that it's actually a bit more complex than this: I am ignoring on purpose things like collision handling, rehashing, etc, you may have a good general idea here https://en.wikipedia.org/wiki/Hash_table

How java HashMap does chaining? how to access all collision values?

I have read somewhere that HashMap uses chaining to resolve collisions. But if that is the case. how can i access all the elements with same key value.
For example :
HashMap<Integer, String> hmap = new HashMap<Integer, String>();
hmap.put(1, "1st value");
hmap.put(1, "2nd value");
hmap.put(1, "3rd value");
hmap.put(1, "4th value");
Now, if I do hmap.get(1) it returns “4th Value”
if Indeed it does chaining like
Key values 1 “4th Value” ---> “3rd Value”--->”2nd Value”---->
“1st Value”
How can I get the other values?
hmap.get(1) only returns the 1st value.
My second question is,
if it does linear chaining. How can I remove any one value for a key. suppose I want to remove “4th value” from my hashmap and want to keep all other values for same key, how can i do it?
if I do
hmap.remove(1);
, it removes the complete chain.
HashMap cannot store multiple values for the same key.
Chaining is used to resolve hash collisions, i.e. situations when different keys have the same hash. So, it's not about storing multiple values with the same key, it's about multiple values whose keys have the same hashes.
Data structure that can store multiple values for the same key is called a multimap. Unfortunately, there is no built-in implementation of multimap in JRE.
If you need a multimap, you can maintain a Map of Lists (as suggested by matsev), or use an existing multimap implementation from a third-party library, such as Google Guava.
See also:
Collision resolution
From the documentation of HashMap.put(K, V):
Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.
What you can do is to put a List as your value, e.g.
HashMap<Integer, List<String>> hmap = new HashMap<Integer, List<String>>();
List<String> list = hmap.get(1);
if (list == null) {
list = new ArrayList<String>();
hmap.put(1, list);
}
list.add("1st value");
list.add("2nd value");
// etc
I don't think HashTable allows duplicate keys. You should read this What happens when a duplicate key is put into a HashMap?
You're obviously looking for a data structure like Guava's MultiMap which allows exactly what you want: Having multiple values per key.
Java's HashMap does not do chaining, as the documentation for put(K, V) clearly states:
public V put(K key, V value)
Associates the specified value with the specified key in this map. If
the map previously contained a mapping for the key, the old value is
replaced.
If you store an existing key in the HashMap then it will override the old value with the new value and put() will return the old value
System.out.println(hmap.put("1",1st value));
System.out.println(hmap); // o/p "1st value"

Categories

Resources