Sunday, November 4, 2012

equals(), hashCode(), toString() [EN]

These three methods are included in the class Object and therefore present in all Java objects, as in Java all objects inherit from ObjectWhat is the purpose of these methods? What is its significance?


The hashCode method is used to define hash function in hash tables. A hash table associated elements <key,value>. The key elements are unordered, the position is determined by function, so as good function is, operations on the table will be more or less efficient.

The equals method is used to compare two instances of the same class and check if they are equal or not. In the same way as hashCode, the importance of a good implementation of the method will have an impact on performance when it's doing operations that require its use, as searching arrays, comparing lists, etc.

The toString method is used to display a representation of the object. This method is not as important as the previous two but is also highly recommended a good implementation for achieving good performance when it's used.


The contract of hashCode() must satisfy that for an instance of an object return the same value each time it's called and is consistent with the equals() method. If two objects are equal must returns the same hashCode. The function equals() must satisfy the properties reflexive, symmetric and transitive.

1:  import java.util.Date;  
2:    
3:    
4:  /**  
5:   * @author Alvaro  
6:   *  
7:   */  
8:  public class Person  
9:  {  
10:    private Long id;  
11:    private String name;  
12:    private String surname;  
13:    private String address;  
14:    private Date birthDate;  
15:    
16:    /**  
17:     * @return the id  
18:     */  
19:    public Long getId()  
20:    {  
21:      return id;  
22:    }  
23:    
24:    /**  
25:     * @param id the id to set  
26:     */  
27:    public void setId(Long id)  
28:    {  
29:      this.id = id;  
30:    }  
31:    
32:    /**  
33:     * @return the name  
34:     */  
35:    public String getName()  
36:    {  
37:      return name;  
38:    }  
39:    
40:    /**  
41:     * @param name the name to set  
42:     */  
43:    public void setName(String name)  
44:    {  
45:      this.name = name;  
46:    }  
47:    
48:    /**  
49:     * @return the surname  
50:     */  
51:    public String getSurname()  
52:    {  
53:      return surname;  
54:    }  
55:    
56:    /**  
57:     * @param surname the surname to set  
58:     */  
59:    public void setSurname(String surname)  
60:    {  
61:      this.surname = surname;  
62:    }  
63:    
64:    /**  
65:     * @return the address  
66:     */  
67:    public String getAddress()  
68:    {  
69:      return address;  
70:    }  
71:    
72:    /**  
73:     * @param address the address to set  
74:     */  
75:    public void setAddress(String address)  
76:    {  
77:      this.address = address;  
78:    }  
79:    
80:    /**  
81:     * @return the birthDate  
82:     */  
83:    public Date getBirthDate()  
84:    {  
85:      return birthDate;  
86:    }  
87:    
88:    /**  
89:     * @param birthDate the birthDate to set  
90:     */  
91:    public void setBirthDate(Date birthDate)  
92:    {  
93:      this.birthDate = birthDate;  
94:    }  
95:  }  

Working with a development environment like Eclipse or Netbeans we have tools that help us to override these methods automatically, only indicating the properties to include in the method. In Eclipse we can do it with ALT + SHIFT + S and selecting Generate hashCode() and equals() ... or Generate toString() ...

We just have to manually select the attributes that we use to define our method.



Although this method generates a code that satisfies the conditions of the contract, it can generate a lot of code if the object has many attributes.


1:    /* (non-Javadoc)  
2:     * @see java.lang.Object#hashCode()  
3:     */  
4:    @Override  
5:    public int hashCode()  
6:    {  
7:      final int prime = 31;  
8:      int result = 1;  
9:      result = (prime * result) + ((address == null) ? 0 : address.hashCode());  
10:      result = (prime * result) + ((birthDate == null) ? 0 : birthDate.hashCode());  
11:      result = (prime * result) + ((id == null) ? 0 : id.hashCode());  
12:      result = (prime * result) + ((name == null) ? 0 : name.hashCode());  
13:      result = (prime * result) + ((surname == null) ? 0 : surname.hashCode());  
14:    
15:      return result;  
16:    }  
17:    
18:    /* (non-Javadoc)  
19:     * @see java.lang.Object#equals(java.lang.Object)  
20:     */  
21:    @Override  
22:    public boolean equals(Object obj)  
23:    {  
24:      if (this == obj)  
25:      {  
26:        return true;  
27:      }  
28:    
29:      if (obj == null)  
30:      {  
31:        return false;  
32:      }  
33:    
34:      if (getClass() != obj.getClass())  
35:      {  
36:        return false;  
37:      }  
38:    
39:      Person other = (Person) obj;  
40:    
41:      if (address == null)  
42:      {  
43:        if (other.address != null)  
44:        {  
45:          return false;  
46:        }  
47:      }  
48:      else if (!address.equals(other.address))  
49:      {  
50:        return false;  
51:      }  
52:    
53:      if (birthDate == null)  
54:      {  
55:        if (other.birthDate != null)  
56:        {  
57:          return false;  
58:        }  
59:      }  
60:      else if (!birthDate.equals(other.birthDate))  
61:      {  
62:        return false;  
63:      }  
64:    
65:      if (id == null)  
66:      {  
67:        if (other.id != null)  
68:        {  
69:          return false;  
70:        }  
71:      }  
72:      else if (!id.equals(other.id))  
73:      {  
74:        return false;  
75:      }  
76:    
77:      if (name == null)  
78:      {  
79:        if (other.name != null)  
80:        {  
81:          return false;  
82:        }  
83:      }  
84:      else if (!name.equals(other.name))  
85:      {  
86:        return false;  
87:      }  
88:    
89:      if (surname == null)  
90:      {  
91:        if (other.surname != null)  
92:        {  
93:          return false;  
94:        }  
95:      }  
96:      else if (!surname.equals(other.surname))  
97:      {  
98:        return false;  
99:      }  
100:    
101:      return true;  
102:    }  


Why prime numbers are often used to generate the hashCode?
Because it’s a good strategy to avoid many collisions occur when the table has many elements. As primes are not divisible by other numbers, if we multiply the different attributes used in the role of prime numbers, is much more unlikely that different objects generate the same value.

If we work in a database context and we are using an ORM framework (like Hibernate or iBATIS), and we have POJOs for mapping the tables, we can consider the fact of having primary keys. The value of a primary key is unique to each element. If we have a POJO with an attribute that represents the primary key, this attribute will be sufficient to implement hashCode() and equals() because is a single and comparable value.

In the example would be implemented manually as follows:


1:    @Override  
2:    public int hashCode()  
3:    {  
4:      return id.hashCode();  
5:    }  
6:    
7:    @Override  
8:    public boolean equals(Object obj)  
9:    {  
10:      boolean result = false;  
11:    
12:      if ((obj != null) && obj instanceof Person)  
13:      {  
14:        Person pojo = (Person) obj;  
15:        result = (id != null) && id.equals(pojo.getId());  
16:      }  
17:    
18:      return result;  
19:    }  

There are some libraries that help overwrite the three functions efficiently and minimizing code size. One of the best known is Apache Commons Lang which has among other utilities HashCodeBuilder, EqualsBuilder y ToStringBuilder.




Suppose that in the previous example we want to use only the ID corresponding to the PK using these libraries. In addition we will take a step further encapsulating this functionality in abstract class using a generic type. So we’ll can use in all POJOs that we have and so have it well defined for all methods. Thus the id can be any primitive type (String, Integer, Long, etc..) or even a composite id, for example another bean.
1:  /**  
2:   * @author Alvaro  
3:   *  
4:   * @param <T>  
5:   */  
6:  public abstract class AbstractGenericModel<T extends Comparable<T>>  
7:  {  
8:    private T id;  
9:    
10:    /**  
11:    * @return the id  
12:    */  
13:    @Id  
14:    @Column(name = "ID")  
15:    public T getId()  
16:    {  
17:      return id;  
18:    }  
19:    
20:    /**  
21:     * @param id the id to set  
22:     */  
23:    public void setId(T id)  
24:    {  
25:      this.id = id;  
26:    }  
27:    
28:    /**  
29:     *{@inheritDoc}  
30:     */  
31:    @Override  
32:    public int hashCode()  
33:    {  
34:      return new HashCodeBuilder().append(id).toHashCode();  
35:    }  
36:    
37:    /**  
38:     *{@inheritDoc}  
39:     */  
40:    @SuppressWarnings("unchecked")  
41:    @Override  
42:    public boolean equals(Object obj)  
43:    {  
44:      boolean result = false;  
45:    
46:      if ((obj != null) && obj instanceof Person)  
47:      {  
48:        AbstractGenericModel<T> pojo = (AbstractGenericModel<T>) obj;  
49:        new EqualsBuilder().append(id, pojo.getId());  
50:      }  
51:    
52:      return result;  
53:    }  
54:  }  


If the name of the id in database is different from "ID" we can overwrite it from the child class. So if we implement the initial class Person, assuming that it's mapped to a PERSONS table, using the generic implementation, it would look like this:

1:    
2:  import org.apache.commons.lang.builder.EqualsBuilder;  
3:    
4:  import java.util.Date;  
5:    
6:  import javax.persistence.Column;  
7:  import javax.persistence.Entity;  
8:  import javax.persistence.Table;  
9:    
10:    
11:  /**  
12:   * @author Alvaro  
13:   *  
14:   */  
15:  @Entity  
16:  @Table(name = "PERSONS", schema = "TEST")  
17:  public class Person extends AbstractGenericModel<Long>  
18:  {  
19:    private String name;  
20:    private String surname;  
21:    private String address;  
22:    private Date birthDate;  
23:    
24:    /**  
25:     * @return the name  
26:     */  
27:    @Column(name = "NAME")  
28:    public String getName()  
29:    {  
30:      return name;  
31:    }  
32:    
33:    /**  
34:     * @param name the name to set  
35:     */  
36:    public void setName(String name)  
37:    {  
38:      this.name = name;  
39:    }  
40:    
41:    /**  
42:     * @return the surname  
43:     */  
44:    @Column(name = "SURNAME")  
45:    public String getSurname()  
46:    {  
47:      return surname;  
48:    }  
49:    
50:    /**  
51:     * @param surname the surname to set  
52:     */  
53:    public void setSurname(String surname)  
54:    {  
55:      this.surname = surname;  
56:    }  
57:    
58:    /**  
59:     * @return the address  
60:     */  
61:    @Column(name = "ADDRESS")  
62:    public String getAddress()  
63:    {  
64:      return address;  
65:    }  
66:    
67:    /**  
68:     * @param address the address to set  
69:     */  
70:    public void setAddress(String address)  
71:    {  
72:      this.address = address;  
73:    }  
74:    
75:    /**  
76:     * @return the birthDate  
77:     */  
78:    @Column(name = "BIRTH_DATE")  
79:    public Date getBirthDate()  
80:    {  
81:      return birthDate;  
82:    }  
83:    
84:    /**  
85:     * @param birthDate the birthDate to set  
86:     */  
87:    public void setBirthDate(Date birthDate)  
88:    {  
89:      this.birthDate = birthDate;  
90:    }  
91:    
92:    /**  
93:     * {@inheritDoc}  
94:     */  
95:    @Override  
96:    public String toString()  
97:    {  
98:      return new EqualsBuilder().append("id", getId()).append("name", name).append("surname", surname)  
99:                   .append("address", address).append("birthDate", birthDate).toString();  
100:    }  
101:  }  


We have to overwrite these three methods when it be necessary for our application in the most optimal way and writing the less quantity of code. So when it's doing use of objects by algorithms, it will have a positive impact. The will be more efficient, and our code will be clear and readable by others.

No comments:

Post a Comment