I'm trying to use HashSet to store objects of a class that I created, but apparently the same objects seem to have two different hashes, which is why the contains method does not realize that the object is already in the HashSet. This leads to my program running out of heap memory.
I don't think I'm doing anything wrong, but I wanted a second opinion anyway. I've done similar operations before which all worked fine, which makes this particularly annoying. I'd appreciate any help.
Here's my code
move1 = new Move(t,s);
if(move1.hashCode()==new Move(t,s).hashCode())
System.out.println("match");
move2 = new Move(s,t);
moves.add(move1);
moves.add(move2);
if(moves.contains(new Move(t,s)))
System.out.println("match found");
Here's the Move class:
public class Move {
private int move1;
private int move2;
Move(int m1, int m2)
{
move1 = m1;
move2 = m2;
}
public String toString()
{
return String.valueOf(move1)+" "+String.valueOf(move2);
}
}
Here's the output I get
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.HashMap.addEntry(HashMap.java:797)
at java.util.HashMap.put(HashMap.java:431)
at java.util.HashSet.add(HashSet.java:194)
at makeMove.<init>(makeMove.java:33)
You need to override the Object#hashCode() method in the Move class to let it return the same hashCode() value for the state of the Move instance. Don't forget to override Object#equals() as well.
See also:
Overriding equals and hashCode in Java
Hint: if you're using an IDE like Eclipse, you can also just autogenerate them. Rightclick somewhere the Move class, choose Source > Generate hashCode() and equals(). Here is how it look like then:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + move1;
result = prime * result + move2;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Move other = (Move) obj;
if (move1 != other.move1)
return false;
if (move2 != other.move2)
return false;
return true;
}
HashSet will determine equality based on calling hashCode() and equals(). You have not implemented these, so you'll inherite them from Object. The hashCode and equals methods of Object is just based on whether the references are equal.
That's why if(move1.hashCode()==new Move(t,s).hashCode()) is false. move1 is a different instance than the instance created by calling new Move(t,s).hashCode()
You'll need to implement hashCode and equals in your Move class.
e.g.(though perhaps non-optimal, and you might want a null safe equals - have your IDE generate them if it can)
public int hashCode() {
return move1 ^ move2 +;
}
public boolean equals(Object o) {
if(!other instanceof Move)
return false;
Move other = (Move)o;
return other.move1 == move1 && other.move2 == move2;
}
You have to override equals() and hashCode().
This may be an option.
import static java.lang.System.out;
public class Move {
private int move1;
private int move2;
Move(int m1, int m2) {
move1 = m1;
move2 = m2;
}
public String toString() {
return String.valueOf(move1)+" "+String.valueOf(move2);
}
public int hashCode() {
return move1 * 31 + move2 * 31;
}
public boolean equals( Object other ) {
if( this == other ) { return true; }
if( other instanceof Move ) {
Move m2 = ( Move ) other;
return this.move1 == m2.move1 && this.move2 == m2.move2;
}
return false;
}
public static void main( String [] args ) {
out.println( new Move(2,3).equals( new Move(2,3)));
out.println( new Move(1,1).hashCode() == new Move(1,1).hashCode() );
}
}
You have to define if the order of the move is relevant ( 1,2 isequals to 2,1 or not )
For more information:
What issues should be considered when overriding equals and hashCode in Java?
Related
This question already has answers here:
How the equals() method works
(8 answers)
What issues should be considered when overriding equals and hashCode in Java?
(11 answers)
Closed 5 years ago.
Consider:
class Dog{
int height;
int weight;
String name;
}
public class DogTest {
public static void main(String[] args) {
Dog one = new Dog();
one.height=4;
one.name="fudo";
one.weight =2;
Dog two = new Dog();
two.height=4;
two.name="fudo";
two.weight =2;
if (one.equals(two)){
System.out.println("True");
}
else{
System.out.println("False");
}
}
}
Why does this output "False"? If it is by default in Java that "all objects are not equal even if they have same values" then how can I "persuade" Java that these two objects actually are equal?
Okay, even if two dogs have same name, height, weight one could be Dalmatian and the other one pit bull, and even if they are the same "race", in nature, they can always be different from one another.
PS: I understand that by saying if (one==two) {} we are comparing if they both refer to the same object on the heap, .equals on string's compares if they have same characters in the same order.
The equals method by default says "Is this the same object in memory?" unless you override it.
You didn't override it.
The behavior didn't change.
You'll want to add a new method like this
public boolean equals(Object o) {
if(o instanceof Dog) {
Dog d = (Dog)(o);
Dog t = this;
return t.height == d.height && t.weight == d.weight && t.name.equals(d.name);
}
return false;
}
Stephan brings up a good point - never, ever, ever implment equals without hashCode. Always use the same fields in both.
public int hashCode() {
int hash = name.hashCode();
hash = hash * 31 + weight;
hash = hash * 31 + height;
return hash;
}
You have to override your equals method in your Dog class. If not you are just comparing if those objects are the same instance in memory.
Here is an implementation of how to do this:
class Dog{
int height;
int weight;
String name;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Dog)) return false;
Dog dog = (Dog) o;
if (height != dog.height) return false;
if (weight != dog.weight) return false;
return name != null ? name.equals(dog.name) : dog.name == null;
}
}
Any IDE allows you to generate a hashcode and equals in an automatic way as getters and setters.
In your case without hashcode and equals, you will get false, because the objects cannot being at the same memory location.
Your (working) example below:
class Dog{
int height;
int weight;
String name;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + height;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + weight;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dog other = (Dog) obj;
if (height != other.height)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (weight != other.weight)
return false;
return true;
}
public static void main(String[] args) {
Dog one = new Dog();
one.height=4;
one.name="fudo";
one.weight =2;
Dog two = new Dog();
two.height=4;
two.name="fudo";
two.weight =2;
if (one.equals(two)){
System.out.println("True");
}
else{
System.out.println("False");
}
}
}
Result:
To generate hashCode and equals in automatic way in Eclipse:
Right click in your class and:
I am writing a class called Coord. I have created a constructor:
public final int r,c;
public Coord (int r, int c){
this.r = r;
this.c = c;
}
I also did another two methods
//Creates and returns a new Coord value with the same row/column
public Coord copy(){
Coord copy = new Coord (r,c);
return copy;
}
//Given another object, is it also a Coord with the same row and column values?
public boolean equals(Object o){
return this==o; //this may be incorrect.
}
Now I can not pass some test cases as following:
Coord c = new Coord (5,10);
#Test (timeout=2000) public void coord() {
assertEquals(c, c.copy());
assertEquals(c, c);
assertFalse(c.equals(new Coord (2,3))); // #(5,10) != #(2,3).
assertFalse(c.equals("hello")); // must work for non-Coords.
}
I think the problem may arise from my boolean equals method, but I have tried a lot I still cannot pass the test. Is there a deep equal issue here? Can someone help me?
Is there a deep equal issue here?
Well yes, your equals method just checks whether the value passed to it is the same reference. Your comment says what you want to do:
//Given another object, is it also a Coord with the same row and column values?
So that's what you need to implement:
#Override public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o.getClass() != getClass()) {
return false;
}
Coord other = (Coord) o;
return other.r == r && other.c == c;
}
I'd also encourage you to make the class final (in which case you can use instanceof instead of calling getClass()) and you need to implement hashCode() to be consistent with equals too. For example:
#Override public int hashCode() {
int hash = 23;
hash = hash * 31 + r;
hash = hash * 31 + c;
return hash;
}
I am using a SparseIntArray and I am puzzled by this behavior:
public static SparseIntArray getArray()
{
SparseIntArray result = new SparseIntArray();
result.append(0, 99);
result.append(1, 988);
result.append(2, 636);
return result;
}
public static void testArray()
{
SparseIntArray first = getArray();
SparseIntArray second = getArray();
if( first.equals(second) )
{
Log.v(TAG,"first "+first.toString()+" == second "+second.toString());
}
else
{
Log.v(TAG,"first "+first.toString()+" != second "+second.toString());
}
}
Output:
11-06 14:53:15.011: V/fileName(6709): first {0=99, 1=988, 2=636} != second {0=99, 1=988, 2=636}
I know that using == between two objects will compare the object addresses, which in this case are different, but here I am using SparseIntArray.equals(Object other) and the intended result is not unexpected.
I am sure I can roll my own compare method, but it sounds kind of silly. What is the point of having a base class Object.equals(Object other) method if we cant rely on it?
Can someone point to any mistake?
I just searched for the code of SparseIntArray. If you are referring to android.util.SparseIntArray, it doesn't override equals, which means it uses the default implementation of Object class, which compares the references.
What is the point for having a base class Object.equals(Object other) method if we cant rely on it?
Actually, you can't rely of the base class Object.equals, since it does precisely what you don't want to do:
public boolean equals(Object obj)
{
return (this == obj);
}
It's up to the writers of any class to decide whether to override equals and give a different implementation.
#Eran is right, Object.equals(Object) does not cut it.
I have made a simple static method to compare two instances
public static boolean compareSame( SparseIntArray first, SparseIntArray second )
{
// compare null
if( first == null )
{
return (second == null);
}
if( second == null )
{
return false;
}
// compare count
int count = first.size();
if( second.size() != count )
{
return false;
}
// for each pair
for( int index = 0; index < count; ++index )
{
// compare key
int key = first.keyAt(index);
if( key != second.keyAt(index))
{
return false;
}
// compare value
int value = first.valueAt(index);
if( second.valueAt(index) != value)
{
return false;
}
}
return true;
}
I will probably end up deriving my own version of SparseIntArray and override the equals method, I think this is more clean.
[EDIT] Here is the code for an sub-class implementing equals
import android.util.SparseIntArray;
public class SparseIntArrayComparable extends SparseIntArray {
#Override
public boolean equals( Object obj ) {
if( obj instanceof SparseIntArray ) {
SparseIntArray other = (SparseIntArray)obj;
// compare count
int count = size();
if( count != other.size() )
return false;
// for each pair
for( int index = 0; index < count; ++index ) {
if( keyAt(index) != other.keyAt(index))
return false;
if( valueAt(index) != other.valueAt(index) )
return false;
}
return true;
}
else
return false;
}
}
We all know that if equals method returns true, then two objects are equal.
Can anybody give an example where 2 objects have the same hash value but they are actually different?
I'm assuming you're familiar with the contract(s) associated with overriding equals() and hashCode(), and the implications of a collision-prone hashCode implementation. Given that, the following trivial example uses an object that holds two Integers and implements a very simple hashCode, and demonstrates how easy it is to have two objects that aren't equal but have the same hashCode. Providing a more sophisticated hashCode algorithm can alleviate this.
The output of running main is:
hashCodes: ih1: 6, ih2: 6
equals: false
Example code:
package example.stackoverflow;
public class IntHolder
{
private Integer primaryData;
private Integer secondaryData;
public IntHolder(Integer primaryData, Integer secondaryData)
{
this.primaryData = primaryData;
this.secondaryData = secondaryData;
}
#Override
public int hashCode()
{
return ((primaryData == null) ? 0 : primaryData.hashCode()) +
((secondaryData == null) ? 0 : secondaryData.hashCode());
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IntHolder other = (IntHolder) obj;
if (primaryData == null)
{
if (other.primaryData != null)
return false;
}
else if (!primaryData.equals(other.primaryData))
return false;
if (secondaryData == null)
{
if (other.secondaryData != null)
return false;
}
else if (!secondaryData.equals(other.secondaryData))
return false;
return true;
}
public static void main(String[] args)
{
IntHolder ih1 = new IntHolder(1, 5);
IntHolder ih2 = new IntHolder(3, 3);
System.out.println("hashCodes: ih1: " + ih1.hashCode() + ", ih2: " + ih2.hashCode());
System.out.println("equals: " + ih1.equals(ih2));
}
}
For reference, Eclipse's auto-generated hashCode() for the IntHolder class is:
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result
+ ((primaryData == null) ? 0 : primaryData.hashCode());
result = prime * result
+ ((secondaryData == null) ? 0 : secondaryData.hashCode());
return result;
}
String str1="abcdef";
String str2="abcdfG";
They both have the same hashcode and equals method returns false.
public class Employee {
protected long employeeId;
public boolean equals(Object o){
if(o == null) return false;
if(!(o instanceof) Employee) return false;
Employee other = (Employee) o;
return this.employeeId == other.employeeId;
}
public int hashCode(){
return (int) this.employeeId;
}
}
In this example, we have overridden the equals method - two employees are equal when they will have same employee id.
If two Employee objects are equal, they will also have the same hash code.
Your Ans -
In this example, we also implemented the hash code - hashcode is the employeeId that is rounded down to an int. That means that many employee id's could result in the same hash code, but these Employee objects would still not be equal, since they don't have the same employee id.
I'm working on a polynomial calculator. My problem is with the equals method. Here is the relevant code:
public class Poly{
Term[] terms;
//Constructors-------------------------------------------
public Poly() {}
public Poly(ArrayList<Term> Terms) {
terms = Terms.toArray(new Term[Terms.size()]);
Arrays.sort(terms, new TermComparator());
}
//Methods-------------------------------------------------
public boolean equals(Poly x) {
boolean q=false;
if(this == x){
q=true;
}
return q;
}
//used in constructor to order terms
class TermComparator implements Comparator<Term> {
#Override
public int compare(Term t1, Term t2) {
return t2.getExp() - t1.getExp();
}
}
}
The equals method always returns false even when two Poly objects have the same value. Can anyone help please?
Your Poly class equals method should be like below
#Override
public boolean equals(Object obj) {
if (this == obj) //checking both are same instance
return true;
if (obj == null) // checking obj should not be null
return false;
if (getClass() != obj.getClass()) //checking both objects from same class
return false;
Poly other = (Poly) obj;
return Arrays.equals(terms, other.terms); //checking all the array values
}
if you are adding Poly objects to collection you need to implement hash code method too.
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(terms);
return result;
}
Please refer
Why do I need to override the equals and hashCode methods in Java?
How should equals and hashcode be implemented when using JPA and Hibernate
It seems you need the following 2 changes:
Do not compare references using code as follows:
if(this == x){
q=true;
}
You need to compare the content of the object - the contents of terms in your case.
When overriding the equals method, you'd better override the hashcode method as well.
My solution involved creating an equals method in the term class first. You would then use that equals method to write the equals method in the polynomial class. So here's the code for the equals method for terms:
public boolean equals(Term x){
boolean a= false;
int expThis = this.getExp();
int coefThis = this.getCoeff();
int expX = x.getExp();
int coefX = x.getCoeff();
if(expThis==expX && coefThis==coefX){
a=true;
}
return a;
}
My polynomial constructor already organizes all terms in decreasing order. If you have polynomials in order then all you have to do is first check that the two polynomials are the same size and then loop through all the terms of the two polynomials, using the equals method from the term class to compare terms. So here's the code for the equals method for polynomials:
public boolean equals(Object obj) {
boolean w=false;
Poly other = (Poly) obj;
int L1 = other.terms.length;
int L2 = this.terms.length;
if(L1==L2){
for(int q=0; q<L1; q++){
Term a=other.terms[q];
Term b=this.terms[q];
if(a.equals(b)==true){
w=true;
}
else{
w=false;
break;
}
}
}
return w;
}