According to the official documentation for the Java Hashtable class (https://docs.oracle.com/javase/7/docs/api/java/util/Hashtable.html), the get() opperation will return one of it's recorded values, if said value has a key that returns true when the parameter is fed into that key's equals() opperation.
So, in theory, the following code should return "Hello!" for both of the Hashtable's get() queries:
public static class Coordinates implements Serializable {
private int ex;
private int why;
public Coordinates(int xCo, int yCo) {
ex = xCo;
why = yCo;
}
public final int x() {
return ex;
}
public final int y() {
return why;
}
public boolean equals(Object o) {
if(o == null) {
return false;
} else if(o instanceof Coordinates) {
Coordinates c = (Coordinates) o;
return this.x() == c.x() && this.y() == c.y();
} else {
return false;
}
}
}
Hashtable<Coordinates, String> testTable = new Hashtable<Coordinates, String>();
Coordinates testKey = new Coordinates(3, 1);
testTable.put(testKey, "Hello!");
testTable.get(testKey); //This will return the "Hello" String as expected.
testTable.get(new Coordinates(3, 1)); //This will only return a null value.
However, get() doesn't work as it's supposed to. It seems to only work if you litterally feed it the exact same object as whatever was the original key.
Is there any way to correct this and get the Hashtable to function the way it's described in the documentation? Do I need to make any adjustments to the custom equals() opperation in the Coordinates class?
To be able to store and retrieve objects from hash-based collections you should implement/oeverride the equals() as well as hashCode() methods of the Object class. In your case, you have overridden the equals() and left the hashCode() method to its default implementation inherited from the Object.
Here is the general contract of the hashCode() method you must consider while implementing it:
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 hash tables.
And here is an example implementation that is generated from my IDE (as alread mentioned by #Peter in the comment area) which you can modify to suit your requirements:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ex;
result = prime * result + why;
return result;
}
Related
Hi i'm just beginner learning about abstract classes & interfaces.
Everything we build our prof is testing by creating clones and comparing objects.
I've learned overriding the equals() method the detailed way…
#Override
public boolean equals(Object obj){
if (this == obj){
return true;
}
else if (obj == null){
return false;
}
...
else {
Obj x = (*Superclass*)obj;
…
}
I was now wondering if I could replace this long way by a short Version where I change the toString method, do a hashCode of the toString method and compare the hashCodes of my Objects (original & clone).
Would this be ok to do or is there any reason i shouldn't do it?
Would I be able to inherit the toString, hashCode and equals method and just adjust the clone() in subclasses if we assume that the subclasses use the same variables?
My idea was following
public abstract *Superclass*{
public String name; //would be private in org. code
public int hp; //would be private in org. code
public Superclass(){
}
#Override
public Superclass clone(){
return this; //(not sure if this is ok to use)
}
#Override
public String toString(){
Class temp = getClass();
return temp.getName() + this.name + " " + this.hp;
}
#Override
public int hashCode(){
int hcModify = 10;
int hcCurrent = this.toString().hashCode();
return hcModify * hcCurrent;
}
#Override
public boolean equals(Object obj){
return this.hashCode() == obj.hashCode())
}
}
So the first thing to note is that your equals method will throw an error if obj is null - you can't use any . operators on null.
Your clone method is dangerous if there's mutability in play - mutability means "values can be changed". Because it just returns a reference, changes will be reflected in both the original and "cloned" values (because they're the same.) This is not what most developers would expect. (I suggest looking up deep vs. shallow clones, which is related.)
x = new Thing()
y = thing.clone()
x.changeInSomeWay()
//is y now also changed?
The method of using hash codes for equality is not necessarily good or bad - it depends on the relation of the object to its hash and toString functions, and if there are colisions. Some objects will have hash or toString colisions, where different objects will have the same hash or string representations - particularly large or complex objects, where those representations don't include all of the data that you'd want to be reflected in an equality check.
Yours is actually an example of this. You're using an int hashcode, which only has 2^32 (or whatever) possible values, while Strings have, in principle, infinite possible values; by the pigeonhole principal, there must therefor be multiple objects with different names but the same hashcode.
In general, it's not a safe practice, and can lead to weird, difficult to diagnose errors.
I'm not sure why you're multiplying by 10?
import java.util.HashMap;
import java.util.Map;
class Geek
{
public String name;
public int id;
Geek(String name, int id)
{
this.name = name;
this.id = id;
}
#Override
public boolean equals(Object obj)
{
// checking if both the object references are
// referring to the same object.
if(this == obj)
return true;
// it checks if the argument is of the
// type Geek by comparing the classes
// of the passed argument and this object.
// if(!(obj instanceof Geek)) return false; ---> avoid.
if(obj == null || obj.getClass()!= this.getClass())
return false;
// type casting of the argument.
Geek geek = (Geek) obj;
// comparing the state of argument with
// the state of 'this' Object.
System.out.println("equals method ....."+(geek.name == this.name && geek.id == this.id));
return (geek.name == this.name && geek.id == this.id);
}
int counter = 0;
#Override
public int hashCode()
{
// We are returning the Geek_id
// as a hashcode value.
// we can also return some
// other calculated value or may
// be memory address of the
// Object on which it is invoked.
// it depends on how you implement
// hashCode() method.
++counter;
System.out.println("counter ::>>> "+counter);
return counter;
}
Driver code:
public static void main (String[] args)
{
Map<Geek, Integer> map = new HashMap<>();
// creating the Objects of Geek class.
Geek g1 = new Geek("aa", 1);
Geek g2 = new Geek("aa", 1);
map.put(g1, g1.id);
map.put(g2, g2.id);
map.forEach((k,v) -> {
System.out.println("key = "+k + "\n value = "+v);
});
/* else
System.out.println("Both Objects are not equal. "); */
}
Here, I am overriding the hashCode() method but still the map contains only one object which is g2. Why didn't the HashMap store two objects, given that my hashcode returns a different integer every time?
Even though my equals() method returns true for the same object, why is the HashMap not storing two objects? Can someone please guide me in this regard?
Your counter variable is an instance variable, so it's initialized to 0 for each Geek instance. Therefore, both g1 and g2 have the same hashCode() of 1 when you put them in the Map, and are considered identical by the HashMap, since they are equal to each other based on your equals implementation.
If you change counter to be static, you will get different hashCode() for the 2 instances of Geek, and they would be stored in separate map entries.
That said, your hashCode() implementation is very bad. If you call hashCode() for the same instance multiple times, you'll get a different result each time! This means that if you attempt to put g1 twice in the Map, it will probably put it twice, since the second put will see a different hashCode(), and will therefore search for the key in a different bucket.
hashCode() function must not change when you call it on the same object instance multiple times. You can't generate a new value each time you call it, right now you are doing it by incrementing counter.
As per Object.hashCode() javadoc:
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.
I was reviewing one of Oracle’s Java Certification Practice Exams when I came across the follow question:
Given:
class MyKeys {
Integer key;
MyKeys(Integer k) {
key = k;
}
public boolean equals(Object o) {
return ((MyKeys) o).key == this.key;
}
}
And this code snippet:
Map m = new HashMap();
MyKeys m1 = new MyKeys(1);
MyKeys m2 = new MyKeys(2);
MyKeys m3 = new MyKeys(1);
MyKeys m4 = new MyKeys(new Integer(2));
m.put(m1, "car");
m.put(m2, "boat");
m.put(m3, "plane");
m.put(m4, "bus");
System.out.print(m.size());
What is the result?
A) 2
B) 3
C) 4
D) Compilation fails
My guess was B because m1 and m3 are equal due to their key references being the same. To my surprise, the answer is actually C. Does put() do something that I am missing? Why wouldn’t "plane" replace "car"? Thank you!
With given definition of class i.e
class MyKeys {
Integer key;
MyKeys(Integer k) {
key = k;
}
public boolean equals(Object o) {
return ((MyKeys) o).key == this.key;
}
}
It will result ans = 4, it has only equal method, if you add definition of hashcode then it will result ans=3
class MyKeys {
Integer key;
MyKeys(Integer k) {
this.key = k;
}
#Override
public boolean equals(Object o) {
return ((MyKeys) o).key == this.key;
}
#Override
public int hashCode(){
return key*key;
}
}
Contract of equal and hashcode:
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. If you only override equals() and not hashCode() your class violates this contract.
The problem you will have is with collections where unicity of elements is calculated according to both .equals() and .hashCode(), for instance keys in a HashMap.
If you have two objects which are .equals(), but have different hash codes, you lose!
If we keep this simple, since this is for a Java Certification.
Notice that MyKeys doesn't override hashCode, you know there will be something about it. And I usually try to remember only one thing about Object.hashCode
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
Or in short, every instance will have a distinct hashcode. Meaning that with this code, every new MyKeys will add a new pair in the map.
In reality, this is a bit more complex since the method still return an integer, so the risk of collision is still present (integer doesn't provide infinite amount of values). You can see a bit more about this here.
This explain why the answer is that the map will have a size of 4. Each key inserted is a different instance.
As answered by others, the ans will be 4, reason being not overriding hashcode method.
For a more clear reason, whenever an object is added in hash map, the hashcode of the key is generated, which decides the location of the entry set. The 2 objects m1 and m3 will have different hash codes as the hashcode method is not overridden (usual hashcode behaviour). Different hash code will not create any collision and a new entry is made.
On the contrary, the equals methods is called only after the hashcode method produces the same result, i.e., same hash code.
In case of m2 and m4 also, the 2 objects have different hash codes, hence 2 different entries, with no calling done to the equals method.
Hence, in cases of hashing, it is necessary to overload hashcode method, along with equals.
It will be more clear when we see the implementation of put method of HashMap.
// here hash(key) method is call to calculate hash.
// and in putVal() method use int hash to find right bucket in map for the object.
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
In your code you #Override only equals method.
class MyKeys {
Integer key;
MyKeys(Integer k) {
key = k;
}
public boolean equals(Object o) {
return ((MyKeys) o).key == this.key;
}
}
To achieve output you need to override both hashCode() and equals() method.
I have a problem when retrieving values from a hashmap. The hashmap is declared as follows:
HashMap<TRpair,A> aTable = new HashMap<TRpair,A>();
I then put 112 values into the map as follows:
aTable.put(new TRpair(new T(<value>),new Integer(<value>)),new Ai());
where Ai is any one of 4 subclasses that extend A.
I then proceed to check what values are in the map, as follows:
int i = 0;
for (Map.Entry<TRpair,A> entry : aTable.entrySet()) {
System.out.println(entry.getKey().toString() + " " + entry.getValue().toString());
System.out.println(entry.getKey().equals(new TRpair(new T("!"),new Integer(10))));
i++;
}
i holds the value 112 at the end, as one would expect and the equality test prints true for exactly one entry, as expected.
However, when I do
System.out.println(aTable.get(new TRpair(new T("!"), new Integer(10))));
null is output, despite the above code snippet confirming that there is indeed one entry in the map with exactly this key.
If it helps, the class TRpair is declared as follows:
public class TRpair {
private final T t;
private final Integer r;
protected TRpair(Integer r1, T t1) {
terminal = t1;
row = r1;
}
protected TRpair(T t1, Integer r1) {
t = t1;
r = r1;
}
#Override
public boolean equals(Object o) {
TRpair p = (TRpair)o;
return (p.t.equals(t)) && (p.r.equals(r));
}
#Override
public String toString() {
StringBuilder sbldr = new StringBuilder();
sbldr.append("(");
sbldr.append(t.toString());
sbldr.append(",");
sbldr.append(r.toString());
sbldr.append(")");
return sbldr.toString();
}
}
the equals() and toString() methods in each of the Ai (extending A) and in the T class are overridden similarly and appear to behave as expected.
Why is the value output from the hashmap aTable null, when previously it has been confirmed that the value for the corresponding key is indeed in the map?
With many thanks,
Froskoy.
The keys/elements for a Hash collection but override hashCode() if euqals is overridden.
You could use.
public int hashCode() {
return t.hashCode() * 31 ^ r.hashCode();
}
BTW: It appears from your code that Integer r cannot be null in which case using int r makes more sense.
From Object.equals()
Note that 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.
IIRC hashmap looks up by hashCode() and not by equality, and since you did not implemented hashcode you use default implementation which is consistent with object pointer equality -
you need to implement proper hashcode function which takes into account "T" parameter as well as integer (or not)
It is good practice that hashCode() and equals() are consistent, but not structly necessary if you know what you are doing.
Ok, I have heard from many places and sources that whenever I override the equals() method, I need to override the hashCode() method as well. But consider the following piece of code
package test;
public class MyCustomObject {
int intVal1;
int intVal2;
public MyCustomObject(int val1, int val2){
intVal1 = val1;
intVal2 = val2;
}
public boolean equals(Object obj){
return (((MyCustomObject)obj).intVal1 == this.intVal1) &&
(((MyCustomObject)obj).intVal2 == this.intVal2);
}
public static void main(String a[]){
MyCustomObject m1 = new MyCustomObject(3,5);
MyCustomObject m2 = new MyCustomObject(3,5);
MyCustomObject m3 = new MyCustomObject(4,5);
System.out.println(m1.equals(m2));
System.out.println(m1.equals(m3));
}
}
Here the output is true, false exactly the way I want it to be and I dont care of overriding the hashCode() method at all. This means that hashCode() overriding is an option rather being a mandatory one as everyone says.
I want a second confirmation.
It works for you because your code does not use any functionality (HashMap, HashTable) which needs the hashCode() API.
However, you don't know whether your class (presumably not written as a one-off) will be later called in a code that does indeed use its objects as hash key, in which case things will be affected.
As per the documentation for Object class:
The general contract of hashCode is:
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.
Because HashMap/Hashtable will lookup object by hashCode() first.
If they are not the same, hashmap will assert object are not the same and return not exists in the map.
The reason why you need to #Override neither or both, is because of the way they interrelate with the rest of the API.
You'll find that if you put m1 into a HashSet<MyCustomObject>, then it doesn't contains(m2). This is inconsistent behavior and can cause a lot of bugs and chaos.
The Java library has tons of functionalities. In order to make them work for you, you need to play by the rules, and making sure that equals and hashCode are consistent is one of the most important ones.
Most of the other comments already gave you the answer: you need to do it because there are collections (ie: HashSet, HashMap) that uses hashCode as an optimization to "index" object instances, an those optimizations expects that if: a.equals(b) ==> a.hashCode() == b.hashCode() (NOTE that the inverse doesn't hold).
But as an additional information you can do this exercise:
class Box {
private String value;
/* some boring setters and getters for value */
public int hashCode() { return value.hashCode(); }
public boolean equals(Object obj) {
if (obj != null && getClass().equals(obj.getClass()) {
return ((Box) obj).value.equals(value);
} else { return false; }
}
}
The do this:
Set<Box> s = new HashSet<Box>();
Box b = new Box();
b.setValue("hello");
s.add(b);
s.contains(b); // TRUE
b.setValue("other");
s.contains(b); // FALSE
s.iterator().next() == b // TRUE!!! b is in s but contains(b) returns false
What you learn from this example is that implementing equals or hashCode with properties that can be changed (mutable) is a really bad idea.
It is primarily important when searching for an object using its hashCode() value in a collection (i.e. HashMap, HashSet, etc.). Each object returns a different hashCode() value therefore you must override this method to consistently generate a hashCode value based on the state of the object to help the Collections algorithm locate values on the hash table.