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

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"

Related

Java - multiple hashmaps pointing to the same key

I have multiple files which contains key=value string pairs. The keys are the same between the files, but the values differs. Each file can have 1000 plus of such pairs.
I want to store each file in a separate hashmap, ie map<KeyString, ValueString>, so if there are five files, then there will be five hashmaps.
To avoid duplicating the keys across each hashmap, is it possible to have each map reference the same key? Note that once the keys are added to the map, it will not be deleted.
I considered making the first file the 'base' as in the flyweight pattern, this base would be the intrinsic set of keys/values. The other remaining files would be the extrinsic set of values, but I don't know how to relate the values back to the base (intrinsic) keys without duplicating the keys?
I am open to a simpler/better approach.
I can think about a simpler approach. Instead of having Map<String, String> think of Map<String, List<String> or directly MultiMap<String, String> from guava.
If each key is in each file and all have values, you could store values from first file at 0th index, from the second at 1st index etc.
If it wouldn't work, I recommend a Collection<Map<String, String>, so you're able to iterate through your Maps. Then when you want to add value to one of the Maps, go through all keySets and if one of them contains that key, just put with object returned from this keySet.
Other solution would be to have a HashSet of keys that have already been put. This would be more efficient.
After reading in the keys, you can use String.intern().
When called, what it does is either:
add the String to the internal pool if it didn't exist already;
return the equivalent String from the pool if it already existed.
String#intern Javadoc
First of all, I don't see the problem with storing multiple instances of your String keys. 5 HashMaps * 1000 keys is a very small number, and you shouldn't have memory issues.
That said, if you still want to avoid duplicating the Strings, you can create the first HashMap, and then you the exact same keys for the other HashMaps.
For example, suppose map1 is the first HashMap and it is already populated with the contents of the first file.
You can write something like this to populate the 2nd HashMap:
for (String key : map1.keySet()) {
map2.put (key, someValue);
}
Of course you will have to find for each key of the first map the corresponding value of the second map. If the keys are not stored in the same order in the input files, this may require some preliminary sorting step.
Perhaps you could hold a static Map<> to map your keys to unique Integers and use those Integers for the keys to your map?
Something like:
class KeySharedMap<K,V> {
// The next key to use. Using Atomics for the auto-increment.
static final AtomicInteger next = new AtomicInteger(0);
// Static mapping of keys to unique Integers.
static final ConcurrentMap<Object,Integer> keys = new ConcurrentHashMap<>();
// The map indexed by Integer from the `keys`.
Map<Integer, V> map = new HashMap<>();
public V get(Object key) {
return map.get(keys.get(key));
}
public V put(Object key, V value) {
// Associate a unique integer for each unique key.
keys.computeIfAbsent(key,x -> next.getAndIncrement());
// Put it in my map.
return map.put(keys.get(key),value);
}
}
Yes, I realise that K is not used here but I suspect it would be necessary if you wish to implement Map<K,V>.

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.

How to use hashmap.put without overriding the previous data? (java)

I have something along the lines of this:
public HashMap<Boolean, String> map = new HashMap();
map.put(this.inverted, "Inverted");
map.put(this.active, "Loading");
System.out.println(map.size());
after seeing that the size was always 1, I realised that using map.put was overriding the previous data. I am currently trying to iterate over the hashmap. Is there a way to add mappings to it without overriding previous ones?
You have declared your HashMap as: -
public HashMap<Boolean, String> map = new HashMap();
Now, just think how many maximum mapping can you have in your map? The answer you can get by thinking of, what all values can your Boolean type take. This is because, you cannot have duplicate keys in a HashMap.
So, probably you got it now, that you can at max have only 2 mappings in your map, one for true and other for false(In fact you can have a 3rd one too, as you can have a mapping for a null key too in your HashMap).
So, in your case, if both this.inverted and this.active are either true or false. Then only one of them can be there, and that would be the later value inserted.
Is there a way to add mappings to it without overriding previous ones?
Probably you have build your HashMap wrongly. You should declare your map as: -
private Map<String, Boolean> map = new HashMap();
And now you can put two mappings as: -
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
It's because this.inverted.equals(this.active) and this.inverted.hashcode()==this.active.hashcode()
Maybe you need redefine the equals method for the key.
In MAP
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value. ---> from Map Api
from your implementation, may be this.inverted and this.active both have same value.
Check the input once. print the keySet, then check.
or change the input to Map<String, Boolean>
As #Frank suggest you should invert your Map.
public final Map<String, Boolean> map = new HashMap<>();
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
System.out.println(map);
If the keys are the same than the previous value is overwritten in a standard Java Map. If you don't want this, you can have a look at a multimap which is implemented for example in commons-collections. It can hold different values for one key.
Hashmap is based on key/value pairs. If your keys are equal (they have the same hashcode), it will behave as you described.
For your use case, reversing your key/value pairs will help you.
public HashMap<String, Boolean> map = new HashMap();
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
System.out.println(map.size());
Get object of innermap ,by passing outer map key .. Then check if key of innermap exists then update values with previous data. else create new object of inner map.

adding the same key twice in the Map

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.

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

Categories

Resources