Can AnyOne Please Explain this HashMap Behaviour - java

My Program is
public class Demo {
public static void main(String[] args) {
List<String> arr = new ArrayList<>();
arr.add("a");
arr.add("b");
Map<List<String>, String> map = new HashMap<>();
map.put(arr, "Ravinda");
System.out.println(map.get(arr));
arr.add("c");
System.out.println(map.get(arr));
}
}
Output is: Ravindra and
null
I am not able to get why the output of second System.out.println is null.
Can anyone please explain.

When you call: map.put(arr, "Ravinda"); you are setting the key of the "Ravinda" value to be a List containing two strings.
By calling arr.add("c"); you are modifying List used earlier to index the "Ravinda" value in your hashmap.
Since the arr List has been changed, it no longer matches the key specified when you called: map.put(arr, "Ravinda");
This is why the hashmap is returning a null value when you try to access it for the second time.
The hashmap still contains the 'Ravinda' value, but this value is indexed against the list containing only the two values.

As this answer explains, you need to be careful when using an object who's hash code is mutable. The hashCode method for an ArrayList varies depending on the elements it contains, so by adding "c" to the list you have changed its hash code.
It's important to note that even if the hash code did not change, the lists would still have to be equal. An object's hash code is not a unique identifier, so internally HashMap uses an equals comparison on the key after retrieving the bucket it is in.
If you have a program that is in this position, you need to take a step back and determine another solution to the problem at hand. There is no reliable method to use a mutable list in a Map that doesn't boil down into reference equality (which makes things pretty pointless anyway).

Related

HashSet.contains returns false when it shouldn't

I have this code:
public class Tray {
private Set<Block> blocks;
private int numColumns;
private int numRows;
//constructor
public Tray (int numRows, int numColumns){
this.numColumns = numColumns;
this.numRows = numRows;
blocks = new HashSet<>();
}
public boolean satisfiesGoal(Tray other){
Block thisBlock = this.blocks.iterator().next();
Block otherBlock = other.blocks.iterator().next();
boolean hashesEqual = thisBlock.hashCode() == otherBlock.hashCode(); // this is true
boolean areEqual = thisBlock.equals(otherBlock) && otherBlock.equals(thisBlock); // this is true
boolean contains = this.blocks.contains(otherBlock); // this is false, why?
return contains;
}
In the main method I have added the 2 Blocks to their respective Trays. According to the debugger, the variables "hashesEqual" and "areEqual" are true, but the boolean "contains" is false. Any ideas as to why the hashes of 2 objects would be equal as well as equal according to the "equals" method, but would not contain the equal object in a HashSet?
This problem happens if you modify the objects in a way that affects their equality and hash codes after adding them to the HashSet. The set will malfunction because the objects are not found in the correct slot of the hash table corresponding to their new value.
Likewise, a HashMap will malfunction if you modify objects used as keys. Similarly with TreeSet and TreeMap. All these data structures can locate objects quickly because each object's value dictates its storage location. The structure becomes wrong if those objects are then modified.
Immutable objects are nicer as set elements and map keys because they avoid this complication.
If you must modify fields that are part of an object's equality, you'll need to temporarily remove it from the set first, and add it again afterwards. Or, use a list as the main container for your objects, and construct a temporary set only when needed.
Are Block objects mutable?
HashSet stores its contents as keys in a HashMap, with an arbitrary Object as the value.
As per this article,
If an object's hashCode() value can change based on its state, then we must be careful when using such objects as keys in hash-based collections [emphasis mine] to ensure that we don't allow their state to change when they are being used as hash keys. All hash-based collections assume that an object's hash value does not change while it is in use as a key in the collection. If a key's hash code were to change while it was in a collection, some unpredictable and confusing consequences could follow. This is usually not a problem in practice -- it is not common practice to use a mutable object like a List as a key in a HashMap.
The code for contains in openjdk is pretty simple - it eventually just calls HashMap.getEntry which uses the hash code and equals to check if the key exists. My guess is that your error is in thinking that the item is in the set already. But you could easily confirm that that is wrong by directly declaring a Set in the code you have posted and adding the items to that collection.
Try adding the following unit test:
Set<Block> blocks = new HashSet<>();
blocks.add(thisBlock);
assertTrue(thisBlock.hashCode() == otherBlock.hashCode() && thisBlock.equals(otherBlock));
assertTrue(blocks.contains(otherBlock));
If the first assertion passes and the second fails then you've found a bug in Java. I find that pretty unlikely.
Also make sure you have the openjdk source code available so you can step into Java methods while debugging. That way you can step into contains and check exactly where it is failing.
Also note that your code this.blocks.iterator().next() creates a new iterator each time the function is called and then returns the first item in the iteration. In other words it picks the first item in the set (note that this is not the least by natural order). If you are trying to iterate through the two sets in sequence and compare values then that's not what your code does at the moment.

HashMap put method

I'm having some trouble when using .put(Integer, String) in Java.
To my understanding, when a collision happens the HashMap asks whether the to value are the same with .equals(Object) and if they are not the two values are stored in a LinkedList. Nevertheless, size() is 1 and the hash iterator only shows one result, the last one.
Apart form this, java HashMap API states:put
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.
THIS IS NOT WHAT I HAVE READ EVERYWHERE.
Thoughts?
public class HashProblema {
public static void main(String[] args) {
HashMap<Integer, String> hash= new HashMap();
hash.put(1, "sdaaaar");
hash.put(1, "bjbh");
System.out.println(hash.size());
for (Object value : hash.values()) {
System.out.println(value);
}
}
}
The output is -:
1
bjbh
Since the mapping for the key exist, it is replaced and the size remains 1 only.
The value gets over written by the new key..the size remains one and the value gets changed..This is how it works, as key values are always unique..You can't map multiple values on 1 key.
The API is the definitive reference and that is what you must believe.
A collision occurs when the hash of of a key already exists in the HashMap. Then the values of the keys are compared, and if they are the different, the entries are placed in a linked list. If the keys are the same, then the old key-value in the HashMap is overwritten.
API documentation should normally be treated as authoritative unless there is very good reason to doubt its accuracy.
You should almost certainly ignore any claim that doesn't flag itself as 'knowingly' at odds with documentation and provide a testable evidence.
I humbly suggest you might be confused about the role of a linked 'collision' list. As it happens HashMap in Java uses a linked-list to store multiple values for which the hash-code of the key is placed in the same 'bucket' as one or more other keys.
A HashMap in Java will always store a Key-Value-Pair. There are no linked lists involved. What you are describing is the general idea of a hash map (often taught in computer science class), but the implementation in Java is different. Here, you will always have one value per key only (the last one you put in that place).
However, you are free to define a HashMap that contains List objects. Though, you have to keep track of duplicates and collisions on your own then

Adding elements to a Hash Map

I was to draw a diagram of a hash table, size 6, after these functions were ran.
add (13) add(21) add(7) add(25)
I'm very unfamiliar with hash tables, but I came up with this.
(7)(13)(21)(25)( )( )
I know that when you add an element to a hash, it is assigned a specific hash code, but I dont understand how to find this. Can someone explain this to me?
To build on #HovercraftFullOfEels answer, not implementing hashCode() properly in an object, will cause HashMap to behave badly when that object is added to it. In other words, a pre-requisite to using HashMap is that all potential members have properly implemented hashCode().
Here is some more information on implementing hashCode(): https://www.google.com/search?q=implementing+hashcode+in+java
define your table like
HashMap newMap = new Hashmap();
then to place elements inside you do
newMap.put(1, "what ever");
Unfortunately, hash tables require two arguments to actually assign values. A hash table works like this:
hashtable.add(key, value);
The key is run through a hash function, and the value is placed into an array at the index provided by the hash function, like so:
public class FauxHashTable {
Object[] storage;
public FauxHashTable(int size)
{
storage = new Object[size];
}
public void add(Object key, Object value)
{
storage[hashFunction(key)] = value;
}
public int hashFunction(Object o)
{
// Does some magic and returns a unique identifying integer of the object! A mathematical function that returns a different integer for EVERY object put in!
}
}
So, your question doesn't make sense to me because you give an add function with only one argument. I think the fact that you added the Java tag is confusing a lot of the answerers here because when one says hash table with Java, one usually means the HashMap class. HashMap is an implementation of a hash table, but I don't think it's quite what you're asking about here.
Since you say "I was to draw", I assume this is homework and so I'm going to try and not straight up give you the answer.

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]

Setting key type in HashMap, how?

hi
I want to create a HashMap (java) that stores Expression, a little object i've created.
How do I choose what type of key to use? What's the difference for me between integer and String? I guess i just don't fully understand the idea behind HashMap so i'm not sure what keys to use.
Thanks!
Java HashMap relies on two things:
the hashCode() method, which returns an integer that is generated from the key and used inside the map
the equals(..) method, which should be consistent to the hash calculated, this means that if two keys has the same hashcode than it is desiderable that they are the same element.
The specific requirements, taken from Java API doc are the following:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.
If you don't provide any kind of specific implementation, then the memory reference of the object is used as the hashcode. This is usually good in most situations but if you have for example:
Expression e1 = new Expression(2,4,PLUS);
Expression e2 = new Expression(2,4,PLUS);
(I don't actually know what you need to place inside your hashmap so I'm just guessing)
Then, since they are two different object although with same parameters, they will have different hashcodes. This could be or not be a problem for your specific situation.
In case it isn't just use the hasmap without caring about these details, if it is you will need to provide a better way to compute the hashcode and equality of your Expression class.
You could do it in a recursive way (by computing the hashcode as a result of the hashcodes of children) or in a naive way (maybe computing the hashcode over a toString() representation).
Finally, if you are planning to use just simple types as keys (like you said integers or strings) just don't worry, there's no difference. In both cases two different items will have the same hashcode. Some examples:
assert(new String("hello").hashCode() == new String("hello").hashCode());
int x = 123;
assert(new Integer(x).hashCode() == new Integer(123).hashCode());
Mind that the example with strings is not true in general, like I explained you before, it is just because the hashcode method of strings computes the value according to the content of the string itself.
The key is what you use to identify objects. You might have a situation where you want to identify numbers by their name.
Map<String,Integer> numbersByName = new HashMap<String,Integer>();
numbersByName.put("one",Integer.valueOf(1));
numbersByName.put("two",Integer.valueOf(2));
numbersByName.put("three",Integer.valueOf(3));
... etc
Then later you can get them out by doing
Integer three = numbersByName.get("three");
Or you might have a need to go the other way. If you know you're going to have integer values, and want the names, you can map integers to strings
Map<String,Integer> numbersByValue = new HashMap<String,Integer>();
numbersByValue.put(Integer.valueOf(1),"one");
numbersByValue.put(Integer.valueOf(2),"two");
numbersByValue.put(Integer.valueOf(3),"three");
... etc
And get it out
String three = numbersByValue.get(Integer.valueOf(3));
Keys and their associated values are both objects. When you get something from a HashMap, you have to cast it to the actual type of object it represents (we can do this because all objects in Java inherit the Object class). So, if your keys are strings and your values are Integers, you would do something like:
Integer myValue = (Integer)myMap.get("myKey");
However, you can use Java generics to tell the compiler that you're only going to be using Strings and Integers:
HashMap<String,Integer> myMap = new HashMap<String,Integer>();
See http://download.oracle.com/javase/1.4.2/docs/api/java/util/HashMap.html for more details on HashMap.
If you do not want to look up the expressions, why do you want them to store in a map?
But if you want to, then the key is that item you use for lookup.

Categories

Resources