I can see static Entry class in Hashmap in java have Equals and Hashcode methods.What is the purpose of these methods.put,get methods we use object hashcode and equals methods....
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
Map.Entry is a representation of a key-value pair, in a map. All of this methods can be used for comparing key-value pairs, not for comparing key-objects.
Entryes are objects too, it's entirely possible to compare them for equality or to use them as keys in a map.
In addition, they are sometimes used as a poor man's Pair class: Didn't Java once have a Pair class?, https://www.baeldung.com/java-pairs, https://www.techiedelight.com/five-alternatives-pair-class-java/ and for this use you really want these implementations of equals and hashCode.
Related
According to what I have read,
to use an object as the key to a hashMap, it has to provide a correct
override and implementation of the equals and hashCode
method. HashMap get(Key k) method calls hashCode method on the key
object and applies returned hashValue to its own static hash
function to find a bucket location(backing array) where keys and
values are stored in form of a nested class called Entry (Map.Entry).
HashMap's internal hash Method defends against poor quality hash
functions.
To test these contracts, I have written a bean class with incorrect but legal implementations of the equals and hashCode method.
The class:
public class HashVO {
private String studentName;
private int age;
private boolean isAdult;
public HashVO(String studentName, int age, boolean isAdult) {
super();
this.studentName = studentName;
this.age = age;
this.isAdult = isAdult;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isAdult() {
return isAdult;
}
public void setAdult(boolean isAdult) {
this.isAdult = isAdult;
}
#Override
public String toString() {
return studentName + " : " + age + " : " + isAdult;
}
#Override
public boolean equals(Object obj) {
return false;
}
#Override
public int hashCode() {
return 31;
}
}
In this case, the hash method of the HashMap,
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
should also return same value everytime because the hashcode always returns 31. So if objects of class HashVO are used as key of a hashMap, the get method should not work, as it should go to the same bucket to retrieve the objects and the equals method always returns false so it will not be able to able to find a match for the key object.
But when I am using this method,
public static void main(String[] args) {
HashMap<HashVO, String> voMap = new HashMap<HashVO, String>();
HashVO vo = new HashVO("Item1", 25, true);
HashVO vo1 = new HashVO("Item2", 12, false);
HashVO vo2 = new HashVO("Item3", 1, false);
voMap.put(vo, "Item");
voMap.put(vo1, "Item1");
voMap.put(vo2, "Item2");
System.out.println(voMap.get(vo));
System.out.println(voMap.get(vo1));
System.out.println(voMap.get(vo2));
}
the output is correct, and showing
Item
Item1
Item2
I want to understand why this correct output is coming even as the Equals and HashCode method implementation is incorrect.
HashMap has a little trick where it compares object references before using equals. Since you're using the same object references for adding the elements and for retrieving them, HashMap will return them correctly.
See Java 7 source here (Java 8 did a pretty big revamp of HashMap but it does something similar)
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
// HERE. Uses == with the key
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
Note that this isn't part of the docs, so don't depend on it.
The HashMap works like this:
1) Index of table cell in which (Key,Value) will save calculates as key.hashCode();
2) Keys in HashMap compare by equals() or by reference comparing.
So, in your situation all pairs of (K,V) will store in one cell of HashMap table as LinkedList.
And you can get them from Map because references for keys will equals
When I run this code why only hashCode() is called not equals method while my hashCode() implementation generate same hashCode for both entries to HashSet?
import java.util.HashSet;
public class Test1 {
public static void main(String[] args) {
Student st=new Student(89);
HashSet st1=new HashSet();
st1.add(st);
st1.add(st);
System.out.println("Ho size="+st1.size());
}
}
class Student{
private int name;
private int ID;
public Student(int iD) {
super();
this.ID = iD;
}
#Override
public int hashCode() {
System.out.println("Hello-hashcode");
return ID;
}
#Override
public boolean equals(Object obj) {
System.out.println("Hello-equals");
if(obj instanceof Student){
if(this.ID==((Student)obj).ID){
return true;
}
else{
return false;
}
}
return false;
}
}
The output for this is:
Hello-hashcode
Hello-hashcode
Ho size=1
The hash set checks reference equality first, and if that passes, it skips the .equals call. This is an optimization and works because the contract of equals specifies that if a == b then a.equals(b).
I attached the source code below, with this check highlighted.
If you instead add two equal elements that are not the same reference, you get the effect you were expecting:
HashSet st1=new HashSet();
st1.add(new Student(89));
st1.add(new Student(89));
System.out.println("Ho size="+st1.size());
results in
$ java Test1
Hello-hashcode
Hello-hashcode
Hello-equals
Ho size=1
Here's the source code from OpenJDK 7, with equality optimization indicated (from HashMap, the underlying implementation of HashSet):
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
// v-- HERE
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
A HashSet uses a HashMap as its backing mechanism for the set. Normally, we would expect that hashCode and equals would be called to ensure that there are no duplicates. However, the put method (which calls a private putVal method to do the actual operation) makes an optimization in the source code:
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
If the hash codes match, it first checks to see if the keys are the same before calling equals. You are passing the same Student object, so they are already ==, so the || operator short-circuits, and equals is never called.
If you passed in a different Student object but with the same ID, then == would return false and equals would be called.
Looking through the source code of HashSet it is using HashMap for all of its operations and the add method performs put(element, SOME_CONSTANT_OBJECT). Here is the source code for the put method for JDK 1.6.0_17 :
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
as you can see it performs a == comparison before using the equals method. Since you are adding the same instance of an object twice the == returns true and the equals method is never called.
Equals is always called after the hashCode method in a java hashed
collection while adding and removing elements. The reason being, if
there is an element already at the specified bucket, then JVM checks
whether it is the same element which it is trying to put.
hashcode() and equals() method
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.
I am trying to create a search algorithm that stores coordinate pairs in a wrapper class called HashSquareSpec. In order to avoid duplicates, and maintain insertion order, I am inserting each HashSquareSpec into a LinkedHashSet. Even though I have overridden the equals() method and hashCode() methods, the LinkedHashSet still accepts two HashSquareSpec objects with
the same coordinate pairs.
public static void main(String [] args)
{
LinkedHashSet<HashSquareSpec> firedShots = new HashLinkedSet<HashSquareSpec>();
HashSquareSpec a = new HashSquareSpec(1,2);
HashSquareSpec b = new HashSquareSpec(2,2);
HashSquareSpec c = new HashSquareSpec(1,2);
HashSquareSpec d = new HashSquareSpec(3,2);
firedShots.add(a);
firedShots.add(b);
firedShots.add(c);
firedShots.add(d);
System.out.println(a.equals((SquareSpec)c));
Iterator l = firedShots.iterator();
while(l.hasNext())
{
System.out.println(l.next().hashCode());
}
}
Output:
true
38444474
38474265
38444474
38504056
HashSquare class
public class HashSquareSpec extends SquareSpec
{
public HashSquareSpec(int sx, int sy)
{
super(sx,sy);
}
public HashSquareSpec(String codeString)
{
super(codeString);
}
#Override
public int hashCode()
{
return this.toString().hashCode();
}
public boolean equals(HashSquareSpec other)
{
if(this.toString().equals(other.toString()))
return true;
else
return false;
}
}
and the super class of HashSquareSpec
public class SquareSpec {
public int x;
public int y;
public SquareSpec(int sx, int sy) {
this.x = sx;
this.y = sy;
}
public SquareSpec(String codeString) {
this.x = Integer.parseInt(codeString.substring(1,2));
this.y = Integer.parseInt(codeString.substring(3,4));
}
public String toString() {
return("(" + x + "," + y + ")");
}
public boolean equals(SquareSpec other) {
return (other.x == this.x &&
other.y == this.y );
}
}
Despite many different hashCode variations and Eclipse equals and hashCode generation, the
firedShots data structure keeps accepting duplicates. What is wrong with my code?
You are on the right track, overriding hashcode and equals, except you are incorrectly overriding the equals method from Object in HashSquareSpec (and SquareSpec). The parameter must be an Object. Because it's not overridden, equals from Object is called, which compares object references to see if they're the same object. They aren't, so the "duplicate" is allowed.
Try:
#Override
public boolean equals(Object other)
{
if(this.toString().equals(other.toString()))
return true;
else
return false;
}
You should also test if other is null and then ensure that other is the same type.
Include the #Override annotation so that the compiler will complain if the method doesn't actually override anything.
It's still accepting because you are not overriding the equals method. You need to override boolean equals(Object). The problem is that you're defining a new method like boolean equals(SquareSpec).
Here is the method that LinkedHashSet#add(T) eventually invokes:
HashMap#put(K, V):
#Override public V put(K key, V value) {
if (key == null) {
return putValueForNullKey(value);
}
int hash = secondaryHash(key.hashCode());
HashMapEntry<K, V>[] tab = table;
int index = hash & (tab.length - 1);
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
if (e.hash == hash && key.equals(e.key)) {
preModify(e);
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
As you can see, it compares using hashCode and equals(Object).
i've checked the source code :
public boolean containsKey(Object key) {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return true;
}
}
return false;
}
public boolean equals(Object obj) {
return (this == obj);
}
from the source code , it shows only the "equal()" method has been called , so if i wanna put an custom Object in a map , i could only have to override the "equal()" method. so i did an experiment , the result is negative ... i have to override both "equals()" and "hashCode()" . so my question is :
why both method (equals() ,hashCode()) has to be override.
does the "==" operation internally calls the "hashCode()" method?
This is AbstractMap's implementation. HashMap, which overrides it, implements it as follows:
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
As you can see, this depends on the hashCode().
Other map types may indeed not depend on this method being overriden.
From Object.equals API: it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes. You can find more detailed explanation in "Effective Java" Item 9: Always override hashCode when you override equals.
No it does not, it is simply pointers comparison, just like comparing two ints
I am new to Java and am using the TreeMap Code example that was on this site but when I try to iterate through the TreeMap, I get a list of null values but when I print the map directly I can see the key/value pairs. How do I rectify this?
import java.util.*;
public class Testing {
public static void main(String[] args) {
HashMap<String,Double> map = new HashMap<String,Double>();
ValueComparator1 bvc = new ValueComparator1(map);
TreeMap<String,Double> sorted_map = new TreeMap<String,Double>(bvc);
map.put("A",99.5);
map.put("B",67.4);
map.put("C",67.4);
map.put("D",67.3);
System.out.println("unsorted map: "+map);
sorted_map.putAll(map);
System.out.println("results: "+sorted_map);
for(String key: sorted_map.keySet())
{
System.out.println(sorted_map.get(key)); //null values-Why?
}
}
}
class ValueComparator1 implements Comparator<String> {
Map<String, Double> base;
public ValueComparator1(Map<String, Double> base) {
this.base = base;
}
// Note: this comparator imposes orderings that are inconsistent with equals.
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
}
}
It doesn't work because your comparator doesn't return 0 when given identical keys, e.g. compare("A", "A"). Change it to
public int compare(String a, String b) {
Double va = base.get(a);
Double vb = base.get(b);
if(va > vb) {
return -1;
} else if(va < vb) {
return 1;
} else {
return a.compareTo(b);
}
}
and it'll work.
Sorry, but your example is a bit upside down. You're putting keys into a sorted map (tree map) but then using the values as keys in the sense that you compare by the values. It looks like you're looking to handle objects that have a key and a value, so here's something you might want to consider. This would certainly be the OOP way to handle "compound" concepts like the one you're modeling with a map.
class Pair implements Comparable<Pair> {
String value;
double key;
Pair(String value, double key) {
this.value = value;
this.key = key;
}
public int compareTo(Pair p) {
return Double.compare(key, p.key);
}
public String toString(Pair p) {
return value + "," + key;
}
}
static void main(String[] args) {
Set<Pair> unsortedSet = new HashSet<Pair>();
unsortedSet.add(new Pair("A", 99.5));
unsortedSet.add(new Pair("B", 67.4));
unsortedSet.add(new Pair("C", 67.4));
unsortedSet.add(new Pair("D", 67.3));
Set<Pair> sortedSet = new TreeSet<Pair>();
sortedSet.add(new Pair("A", 99.5));
sortedSet.add(new Pair("B", 67.4));
sortedSet.add(new Pair("C", 67.4));
sortedSet.add(new Pair("D", 67.3));
System.out.println("Unsorted set: " + unsortedSet);
System.out.println("Sorted set: " + sortedSet);
for (Pair pair : sortedSet) {
System.out.println(pair);
}
}
Since your comparator never returns 0 TreeMap.get() does not work. Still you can iterate over TreeMap entries like this
for (Entry<String, Double> e : sorted_map.entrySet()) {
System.out.println(e);
}
prints
A=99.5
C=67.4
B=67.4
D=67.3
The code of Adam Crume is very important. To explain you more on this, when you call your sorted_map.get(key), it goes to java.util.TreeMap class', getEntryUsingComparator method because you have set the comparator explicitly. This method looks like
final Entry<K,V> getEntryUsingComparator(Object key) {
K k = (K) key;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
Entry<K,V> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
}
return null;
}
Since there is comparison for keys in your compare method of your value comparator, the entry will be null and hence value will also be null because map.get is implemented as
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}