These three methods are included in the class Object and therefore present in all Java objects, as in Java all objects inherit from Object. What 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