In my most recent question, I was informed that I needed to override my equals and hashcode method (among other things). So I took a bit of time to read a few articles and attempted to come up with a proper implementation.
Here are some of the articles I read:
Hashcode on Wikipedia
StackOverflow Overriding equals and hashcode
StackOverflow Why does hashcode use multiplier 31
All the articles were pretty good. Since this is my first time attempting to do this, I just want to be sure I'm not making some simple (or dumb) mistake.
I will be using name to indicate whether my Person object is equivalent to another Person object. The reason for this, is that all the other variables can vary, but the name will always be unique.
Updated to reflect recommended changes
public class Person {
private String name;
private int p_number;
private String address;
//other variables
public Person(String a_name) {
name = a_name;
}
public String getName() {
return name;
}
//other getters and setters
#Override
public boolean equals(Object o) {
if(o == null)
return false;
if(o == this)
return true;
if(!(o instanceof Person))
return false;
Person p = (Person) o;
return name.equals(p.name));
}
#Override
public int hashCode() {
return name.hashCode();
}
}
My questions are as follows:
Did I implement these methods correctly?
Since name is the only variable that determines uniqueness, do I need to bother checking any of the other variables in hashcode?
I was reading on StackOverflow that 31 was chosen as a good prime number awhile ago, but choosing a larger prime is better now? Can someone confirm or deny this claim? (the claim was made in the third link above)
If I haven't implemented these methods properly, how can I change/improve them?
In equals():
if(name.equals(p.getName()))
return true;
Missing false, and you can just:
// Both are Person instances, no need to use the accessor here
return name.equals(p.name);
As to hashCode(), just return name.hashCode();
Also, can name be null? Your methods don't seem to account for that. (edit: answer: no)
As to your questions:
Since name is the only variable that determines uniqueness, do I need to bother checking any of the other variables in hashcode?
NO, certainly not! If your names are equal but ages different, this would lead to a different hash code for objects which are equal, and this violates the Object contract!
I was reading on StackOverflow that 31 was chosen as a good prime number awhile ago, but choosing a larger prime is better now? Can someone confirm or deny this claim? (the claim was made in the third link above)
This, no idea...
To be more complete about the .equals()/.hashCode() contract, I'll mention a utility class from Guava: Equivalence. An implementation of this abstract class for a given
class can allow you to create Sets, and therefore Maps, with these objects as members (keys) as if they had a different implementation of both of these functions:
Equivalence<MyClass> eq = ....;
Set<Equivalence.Wrapper<MyClass>> set = ...;
set.add(eq.wrap(myClassInstance));
This can be actually very useful in some scenarios...
Your equals needs to return a value in all cases, just change the end part to return the name.equals.
#Override
public boolean equals(Object o) {
...
return name.equals(o.getName());
}
Also your hashcode is actually detrimental, all it does is use the name.hashCode() but then multiply it by 31, better to use the default Java string hashcode from the name directly.
#Override
public int hashCode() {
return name.hashCode();
}
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?
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why .equals method is failing on two same value objects?
This is really simple but I'm obviously missing something pretty big here.
Cat cat1 = new Cat("bob");
Cat cat2 = new Cat("bob");
System.out.println(cat1 == cat2);
//false since these references point to diferent objects
System.out.println(cat1.equals(cat2));
//returns false also??
Cat is just a simple class that only has a name.
What is going on here, how does equals() work? I was under the impression that it compared all the fields of the object. It seems that is not the case.
Do I need to overide it for all my classes?
Yes.
java.lang.Object provides very basic implementations of equals() and hashCode(). In particular, they don't go around reflecting on the type of the instance, which would (1) be dreadfully slow, and (2) carry a significant risk of comparing fields that you for various reasons don't want to compare in an equality comparison.
If you want equals() and hashCode() to actually be useful for comparing value equality (rather than reference equality which == does), you'll need to implement both within your own type.
Note that it's not enough to implement just equals(); while technically that will "work", it has the potential to lead to all kinds of weirdness. The simple rule of thumb is: neither or both, but never only one. And they must work on the same fields; if equals() says two instances are equal, then calling hashCode() on both must return the same value (also see the hashCode() contract).
It's also usually a good idea to override toString() with code to provide a meaningful description of the object in question. While not strictly needed, you only need to hit your head against this once in the debugger to realize the value. (Thanks #JonTaylor for mentioning this highly useful, related tidbit.)
And it's .NET that calls it GetHashCode(), while Java uses only hashCode() as the function name...
You need to override equals inside your Cat class. Default equals compares objects on references.
class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
#Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof Cat))
return false;
Cat c = (Cat) obj;
return this.name == null ? false : this.name.equals(c.name);
}
#Override
public int hashCode() {
return this.name == null ? 31 : this.name.hashCode();
}
#Override
public String toString() {
return "Cat Name :" + name;
}
}
References
equals
hashCode
toString
The equals() provided by java.lang.object compares, simply speaking, a unique identifier for the object, though not entirely accurate you can think of it as a memory location, so it will only be true if you compare an object with itself (i.e. two references to the same object in memory)
You need to implement your own equals() method in your Cat class:
class Cat
{
String name;
#Override
public boolean equals(Cat other)
{
if (this.name.equals(other.name))
return true;
return false;
}
}
It would be wise to override hashCode() also, unless this is just a very basic application for homework or something. Also toString() can be useful to override as well.
http://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html
From [Java Doc]
The equals method for class Object implements the most discriminating
possible equivalence relation on objects; that is, for any non-null
reference values x and y, this method returns true if and only if x
and y refer to the same object (x == y has the value true).
Without overriding the equals() method, the objects are different
Hence
System.out.println(cat1.equals(cat2)); // is false
That is because the == compare references and java.lang.Object.equals() translates to this==o thus return same as == in your case
In the case above you are using new operator to create two different objects hence both return false.
If you want .equals() to work as you are expecting, then override theequals() in your Cat class.
I'm trying to figure out what's wrong with this approach, given my particular usage patterns:
#Entity
public class DomainObject {
#Id // + sequence generator
private Long id;
#Override
public boolean equals(Object o) {
// bunch of other checks omitted for clarity
if (id != null) {
return id.equals(o.getId());
}
return super.equals(o);
}
#Override
public int hashCode() {
if (id != null) {
return id.hashCode();
}
return super.hashCode();
}
I've read several posts on the subject and it sounds like you don't want to use a DB-generated sequence value in equals/hashCode because they won't be set until the objects are persisted and you don't want disparate transient instances to all be equal, or the persistence layer itself might break.
But is there anything wrong with falling back to the default Object equals/hashCode (instance equality) for transient objects and then using the generated #Id when you have it?
The worst thing I can think of is, a transient object can't ever be equal to a persistent object, which is fine in my use case - the only time I'm ever putting objects in collections and want contains to work, all the objects are already persistent and all have IDs.
However, I feel like there's something else wrong in a really subtle, non-obvious way deep in the persistence layer but I can't quite figure out what.
The other options don't seem that appealing either:
doing nothing and living with instance equality (default Object.equals): works great for most of my entities, but getting tired of workarounds for the handful of cases when I want a collection with a mix of detached entities (e.g., session scope) and "live" ones from current transaction
using a business key: I have clear natural keys but they're mutable, and this would have
some of the same problems as above (hashCode stability if object changes)
using a UUID - I know that will work, but feels wrong to pollute the DB with artifacts to support java.util collections.
See also:
The JPA hashCode() / equals() dilemma
Should the id field of a JPA entity be considered in equals and hashCode?.
The javadoc of Map writes:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
Whenever an object is persisted, your implementation changes the meaning of equals. As such, any Collections containing that object need no longer work right. In particular, changing the hashcode of an object used as key in a HashMap (or contained in HashSet) is likely to result in future lookups on that Map (Set) not finding the object, and adding that object again to the Map (Set) is likely to succeed, even though under ordinary circumstances, a Map may contain at most one mapping for every given key, and a Set contain every object at most once.
Since it is common to store entities in collections (to express ToMany-associations), that flaw is likely to result in actual hard-to-find bugs.
I therefore strongly recommend against implementing hashcode based on database-generated identifiers.
If you're sure you don't ever need to add an unpersisted entity to a Set or Map key, you can use the ID to test for equality and as the hash code. But if you do this, you could enforce it by throwing an Exception for an unpersisted object:
#Entity
public class DomainObject {
#Id // + sequence generator
private Long id;
#Override
public boolean equals(Object that) {
// bunch of other checks omitted for clarity
if (id != null) {
throw new IllegalStateException("equals() before persisting");
}
if (this == that) {
return true;
}
if (that instanceof DomainObject) {
return id.equals(((DomainObject)that).id);
}
}
#Override
public int hashCode() {
if (id != null) {
throw new IllegalStateException("hashCode() before persisting");
}
return id;
}
}
If you do this, you may see surprise exceptions, where you didn't realize you were relying on these methods on unpersisted objects. You may find this helpful in debugging. You might also find it makes your existing code unusable. Either way, you'll be clearer about how your code works.
One thing you should never do is return a constant for the hash code.
public int hashCode() { return 5; } // Don't ever do this!
Technically, it fulfills the contract, but it's a terrible implementation. Just read the javadocs for Object.hashCode(): …producing distinct integer results for unequal objects may improve the performance of hash tables. (The word "may" here is a serious understatement.)
Yes, you can! But you have to be careful that the hashCode implementation always returns the same constant value as explained in this post:
#Entity
public class Book implements Identifiable<Long> {
#Id
#GeneratedValue
private Long id;
private String title;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return Objects.equals(getId(), book.getId());
}
#Override
public int hashCode() {
return getClass().hashCode();
}
//Getters and setters omitted for brevity
}
This is the only way you can ensure that equals and hashCode are consistent across all entity state transitions.
This question already has answers here:
What issues should be considered when overriding equals and hashCode in Java?
(11 answers)
Closed 7 years ago.
I have a domain object called User. Properties of user include ssoId, name, email, createdBy, createdDate and userRole. Of these, ssoId must be unique in the sense no two users can have the same sso id. So my equals method checks for the sso id and returns either true or false.
#Override public boolean equals(Object o) {
if (!(o instanceof User))
return false;
return user.getSsoId().equals((User)o.getSsoId());
}
What I feel is that this is an incorrect implementation, though it is correct as far as the business rules are concerned. The above implementation will return true for two objects with same sso id but with different values for say name or email or both. Should I change my equals contract to check the equality of all fields? What is your suggestion?
This is (almost) correct for "technical equality", but not for "natural equality". To achieve top technical equality, you should also test the reflexive o == this. It may happen that the object isn't persisted in DB yet and thus doesn't have a technical ID yet. E.g.
public class User {
private Long id;
#Override
public boolean equals(Object object) {
return (object instanceof User) && (id != null)
? id.equals(((User) object).id)
: (object == this);
}
#Override
public int hashCode() {
return (id != null)
? (User.class.hashCode() + id.hashCode())
: super.hashCode();
}
}
For "natural equality" you should rather compare all non-technical properties. For "real world entities" this is after all more robust (but also more expensive) than technical equality.
public class User {
private String name;
private Date birth;
private int housenumber;
private long phonenumber;
#Override
public boolean equals(Object object) {
// Basic checks.
if (object == this) return true;
if (!(object instanceof User)) return false;
// Property checks.
User other = (User) object;
return Objects.equals(name, other.name)
&& Objects.equals(birth, other.birth)
&& (housenumber == other.housenumber)
&& (phonenumber == other.phonenumber);
}
#Override
public int hashCode() {
return Objects.hash(name, birth, housenumber, phonenumber);
}
}
True, that's lot of code when there are a lot of properties. A bit decent IDE (Eclipse, Netbeans, etc) can just autogenerate equals(), hashCode() (and also toString(), getters and setters) for you. Take benefit of it. In Eclipse, rightclick code and peek the Source (Alt+Shift+S) menu option.
See also:
JBoss: Equals and HashCode (in view of persistence)
Hibernate: Persistent Classes - implementing equals() and hashCode()
Related SO question: Overriding equals and hashCode in Java
If in your model ssoid must be unique, that implies that the values for the other fields should not be different for two instances of User. If you want to validate that assumption, you could do so with assertions within the equals method if the overhead is not an issue.
What you're doing seems fine, and you're not violating any of the rules that equals must follow.
You may still want to check other fields, not to change equals's semantics, but to detect an inconsistency in your business logic, and possibly trigger an assertion/exception.
This is a tricky decision to make.
This is a spot i got into when considering hashing a few months ago. I would suggest you read up on what a hash is because it is highly relevant to your answer ... i suggest that you are looking to implement some kind of hash and test its equality.
There are different kinds of equality ... there is the equality of the identity of the object, the equality of the data of the object, the equality of the entire object ... you could also include audit information in there also.
The fact is that 'equal' has many possible meanings.
I resolved this by implementing equal as a strict equality across all fields simply because after asking around it seems to be the intuitive meaning of equals. I then constructed methos for the other kinds of equality i required and defined an interface to wrap these.
I wouldnt test equality on object == this because often you are testing two different objects with the same data which in my book are equal despite them referring to different memory addresses.
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.