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

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.

Related

Map containing itself as a value;

Directly from this java doc:
A special case of this prohibition is that it is not permissible for a
map to contain itself as a key. While it is permissible for a map to
contain itself as a value, extreme caution is advised: the equals and
hashCode methods are no longer well defined on such a map.
Why would the hashcode and equals no longer be well defined on such a map?
The relevant part form AbstractMap.equals which is used by most Map implementations:
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key))) // would call equals on itself.
return false;
}
}
Adding the map as a value would result in an infinite loop.
The full quote of the paragraph from the Java Docs is:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on such a map.
The AbstractMap.hashCode() method uses the hash code of the key value pairs in the map to compute a hash code. Therefore the hash code generated from this method would change every time the map is modified.
The hash code is used to compute the bucket to place a new entry. If the map was used as a key within itself then the computed bucket would be different everytime a new entry is updated/removed/modified. Therefore, future lookups with the map as a key will most likely fail because a differnt bucket is computed from the hash code. Future puts may not be able to detect that the key is already present in the map and then allow multiple entries that have the same key (but in different buckets)
Two maps are equal if the same keys map om the same values. (In some implementations.) So to check equality, the equality of every member should be checked.
Therefore, if a map contains itself, you would get an infinite recurssion of equality checks.
The same goes for hashes, as these can be calculated dependend on the hashes of the elements in the map.
Example:
Map<Int, Object> ma;
Map<Int, Object> mb;
Map<Int, Object> mc;
ma.put(1, ma);
ma.put(2, mb);
mc.put(1, ma);
mc.put(2, mb);
As a human, we can see ma and mc are equal from the definition. A computer would see 2 maps on mb (an empty map) in both maps, which is good. It would see 1 maps on another map in both mc and ma. It checks if these maps are equal. To determine this, it checks again if the two value for 1 are equals. And again.
Note that this is not the case for all implementations. Some implementations might check equality on the location in memory the object is saved, ... But every recursive check will loop infinitely.
To try to explain it:
The equals method will iterate over both Maps and call the equals method of each key and value of the map. So, if a map contains itself, you would keep calling the equals method indefinitely.
The same thing happens with the hash code.
Source: source code of the class AbstractMap

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.

java hashtable with collision resolution

I want to get all the values(multiple) of a particular key.But i m getting only one value?I dont know how to print all the values.Great help if someone correct the code..did not get any help from google search..
import java.util.*;
public class hashing
{
public static void main(String args[])
{
String[] ary=new String[4];
String key;
char[] chrary;
ary[0]=new String("abcdef");
ary[1]=new String("defabc");
ary[2]=new String("ghijkl");
ary[3]=new String("jklghi");
Hashtable<String, String> hasht = new Hashtable<String, String>();
for(int i=0;i<4;i++){
chrary=ary[i].toCharArray();
Arrays.sort(chrary);
key=new String(chrary);
hasht.put(key,ary[i]);
}
Enumeration iterator = hasht.elements();
while(iterator.hasMoreElements()) {
String temp = (String)iterator.nextElement();
System.out.println(temp);
}
}
}
PS:output is defabc jklghi.I want abcdef defabc ghijkl jklghi.
Hashtables can only contain one value per key. To store multiple values, you should either
Store a collection (e.g. List<String> or array) per key. Note that you'll have to initialise the collection prior to insertion of the first value corresponding to that key
Use a MultiMap
Note that many MultiMap implementations exist. The Oracle docs provide a simple implementation too (see here, and search for MultiMap)
The way HashMaps work is that there is only one value for a given key. So if you call:
map.put(key, value1);
map.put(key, value2);
the second line will override the value corresponding to the key.
Regarding your comment about collision, it means something different. Internally, a HashMap stores the key/value pairs in buckets that are defined based on the hashcode of the key (hence the name: hashmap). In the (low probability if the hashcode function is good) case where two non-equal keys have the same hashcode, the implementation needs to make sure that querying the hashmap on one of those keys will return the correct value. That is where hash collision need to be handled.
That's not what collision resolution is meant to do. Collision resolution lets you handle the case when two object with different keys would go into the same "bucket" in the hash map. How this resolution happens is an internal detail of the hash map implementation, not something that would be exposed to you.
Actually, in your case, its not collision, its same key with same hashcode. In general Collision occurs only if two different keys generate same hashcode, This can occur due to a bad implementation of hashCode() method.
Yes, java.util.HashMap will handle hash collisions, If you look at the source code of HashMap, it stores each value in a LinkedList. That means, if two different keys with same hashcode comes in.. then both values will go into same bucket but as two different nodes in the linked list.
Found this link online, which explain How hash map works in detail.
if key is the same, the value will be updated. jvm will not put a new key/value for same keys...
Your Hashtable<String, String> maps one string to one string. So put replaces the value that was before linked to a specific key.
If you want multiple values, you can make a Hashtable<String, []String> or a Hashtable<String, List<String>>.
A cleaner solution would be to use Google's Multimap which allows to associate multiple values to one key :
A collection similar to a Map, but which may associate multiple values
with a single key. If you call put(K, V) twice, with the same key but
different values, the multimap contains mappings from the key to both
values.
You are only putting one String for each key:
hasht.put(key,ary[i]);
So if i=1 that means you put defabc, why do you expect to get multiple values for same key?
Hashtable, like all Map keep only one value per key, the last value you set.
If you want to keep all the values, just print the original array.
String[] ary = "abcdef,defabc,ghijkl,jklghi".split(",");
System.out.println(Arrays.toString(ary));
prints
[abcdef, defabc, ghijkl, jklghi]

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