Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HashCodeBuilder and EqualsBuilder usage style

I often use apache HashCodeBuilder and EqualsBuilder for object equality using reflection, but recently I a colleague told me that using reflection may cause a huge performance hit if the entity contains lots of properties. Worried that I may be using a wrong implementation, my question is, which of the following approach would you prefer? And why?

public class Admin {      private Long id;     private String userName;      public String getUserName() {         return userName;     }      @Override     public boolean equals(Object o) {         if (!(o instanceof Admin)) {             return false;         }         Admin otherAdmin  = (Admin) o;         EqualsBuilder builder = new EqualsBuilder();         builder.append(getUserName(), otherAdmin.getUserName());         return builder.isEquals();     }      @Override     public int hashCode() {         HashCodeBuilder builder = new HashCodeBuilder();         builder.append(getUserName());         return builder.hashCode();     } } 

Vs.

public class Admin {      private Long id;     private String userName;      public String getUserName() {         return userName;     }      @Override     public boolean equals(Object o) {       return EqualsBuilder.reflectionEquals(this, o, Arrays.asList(id));     }      @Override     public int hashCode() {         return HashCodeBuilder.reflectionHashCode(this, Arrays.asList(id));     } } 
like image 489
tintin Avatar asked Jun 06 '12 10:06

tintin


2 Answers

Of course the second option is more elegant and simple. But if you are concerned about performance you should go for first approach. Second method also fails if a security manager is running. I would go for the first option if I was in your situation.

Also there is a mistake in your first approach in generating hashCode:
It should be builder.toHashCode() instead of builder.hashCode(). The latter returns hashcode builder object's hash code.

like image 136
Subin Sebastian Avatar answered Sep 20 '22 14:09

Subin Sebastian


Even though the second option is more attractive (because it is just one line of code) I would choose the first option.

The reason is simply performance. After running a small test I found a very great time difference between them.

In order to sort of get an idea of time, I created this two simple classes:

package equalsbuildertest;  import java.math.BigDecimal; import java.util.Date;  import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;  public class Class1 {      private int field1;      private boolean field2;      private BigDecimal field3;      private String field4;      private Date field5;      private long field6;      public Class1(int field1, boolean field2, BigDecimal field3, String field4,             Date field5, long field6) {         super();         this.field1 = field1;         this.field2 = field2;         this.field3 = field3;         this.field4 = field4;         this.field5 = field5;         this.field6 = field6;     }      public Class1() {         super();     }      public int getField1() {         return field1;     }      public void setField1(int field1) {         this.field1 = field1;     }      public boolean isField2() {         return field2;     }      public void setField2(boolean field2) {         this.field2 = field2;     }      public BigDecimal getField3() {         return field3;     }      public void setField3(BigDecimal field3) {         this.field3 = field3;     }      public String getField4() {         return field4;     }      public void setField4(String field4) {         this.field4 = field4;     }      public Date getField5() {         return field5;     }      public void setField5(Date field5) {         this.field5 = field5;     }      public long getField6() {         return field6;     }      public void setField6(long field6) {         this.field6 = field6;     }      @Override     public boolean equals(Object o) {       return EqualsBuilder.reflectionEquals(this, o);     }      @Override     public int hashCode() {         return HashCodeBuilder.reflectionHashCode(this);     }  } 

And:

package equalsbuildertest;  import java.math.BigDecimal; import java.util.Date;  import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;  public class Class2 {      private int field1;      private boolean field2;      private BigDecimal field3;      private String field4;      private Date field5;      private long field6;      public Class2(int field1, boolean field2, BigDecimal field3, String field4,             Date field5, long field6) {         super();         this.field1 = field1;         this.field2 = field2;         this.field3 = field3;         this.field4 = field4;         this.field5 = field5;         this.field6 = field6;     }      public Class2() {         super();     }      public int getField1() {         return field1;     }      public void setField1(int field1) {         this.field1 = field1;     }      public boolean isField2() {         return field2;     }      public void setField2(boolean field2) {         this.field2 = field2;     }      public BigDecimal getField3() {         return field3;     }      public void setField3(BigDecimal field3) {         this.field3 = field3;     }      public String getField4() {         return field4;     }      public void setField4(String field4) {         this.field4 = field4;     }      public Date getField5() {         return field5;     }      public void setField5(Date field5) {         this.field5 = field5;     }      public long getField6() {         return field6;     }      public void setField6(long field6) {         this.field6 = field6;     }      @Override     public boolean equals(Object obj) {         if (!(obj instanceof Class2)) {             return false;         }         Class2 other = (Class2) obj;         EqualsBuilder builder = new EqualsBuilder();         builder.append(field1, other.field1);         builder.append(field2, other.field2);         builder.append(field3, other.field3);         builder.append(field4, other.field4);         builder.append(field5, other.field5);         builder.append(field6, other.field6);         return builder.isEquals();     }      @Override     public int hashCode() {         HashCodeBuilder builder = new HashCodeBuilder();         builder.append(getField1());         builder.append(isField2());         builder.append(getField3());         builder.append(getField4());         builder.append(getField5());         builder.append(getField6());         return builder.hashCode();      };  } 

That second class is pretty much the same as the first one, but with different equals and hashCode.

After that, I created the following tests:

package equalsbuildertest;  import static org.junit.Assert.*;  import java.math.BigDecimal; import java.util.Date;  import org.junit.Test;  public class EqualsBuilderTest {      @Test     public void test1() {         Class1 class1a = new Class1(1, true, new BigDecimal(0), "String", new Date(), 1L);         Class1 class1b = new Class1(1, true, new BigDecimal(0), "String", new Date(), 1L);         for (int i = 0; i < 1000000; i++) {             assertEquals(class1a, class1b);         }     }      @Test     public void test2() {         Class2 class2a = new Class2(1, true, new BigDecimal(0), "String", new Date(), 1L);         Class2 class2b = new Class2(1, true, new BigDecimal(0), "String", new Date(), 1L);         for (int i = 0; i < 1000000; i++) {             assertEquals(class2a, class2b);         }     }  } 

The test are pretty simple and only serves to measure time.

The results were the following:

  • test1 (2,024 s)
  • test2 (0,039 s)

I chose them to be completely equal in order to have the greatest times. If you choose to do the test with NotEquals conditions you will have shorter time, but keeping a very large time difference too.

I run this tests on a 64-bit Intel Core i5-3317U CPU @1.70GHz x4 with Fedora 21 and Eclipse Luna.

In conclusion, I would not risk such a great performance difference in order to save a couple of lines of code that you can possibly not type anyway using a template (in Eclipse under Windows -> Preferences is found in Java -> Editor -> Templates) such as this:

${:import(org.apache.commons.lang3.builder.HashCodeBuilder, org.apache.commons.lang3.builder.EqualsBuilder)} @Override public int hashCode() {     HashCodeBuilder hashCodeBuilder = new HashCodeBuilder();     hashCodeBuilder.append(${field1:field});     hashCodeBuilder.append(${field2:field});     hashCodeBuilder.append(${field3:field});     hashCodeBuilder.append(${field4:field});     hashCodeBuilder.append(${field5:field});     return hashCodeBuilder.toHashCode(); }  @Override public boolean equals(Object obj) {     if (this == obj) {         return true;     }     if (obj == null) {         return false;     }     if (getClass() != obj.getClass()) {         return false;     }     ${enclosing_type} rhs = (${enclosing_type}) obj;     EqualsBuilder equalsBuilder = new EqualsBuilder();     equalsBuilder.append(${field1}, rhs.${field1});     equalsBuilder.append(${field2}, rhs.${field2});     equalsBuilder.append(${field3}, rhs.${field3});     equalsBuilder.append(${field4}, rhs.${field4});     equalsBuilder.append(${field5}, rhs.${field5});${cursor}     return equalsBuilder.isEquals(); } 
like image 31
Sebastian D'Agostino Avatar answered Sep 18 '22 14:09

Sebastian D'Agostino