HashCodeBuilder和EqualsBuilder的使用方式

tin*_*tin 28 java hibernate apache-commons

我经常使用apache HashCodeBuilder和EqualsBuilder使用反射来实现对象相等,但最近我的一位同事告诉我,如果实体包含许多属性,使用反射可能会导致巨大的性能损失.担心我可能使用了错误的实现,我的问题是,您更喜欢以下哪种方法?为什么?

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

比.

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

ssk*_*ssk 19

当然第二种选择更优雅和简单.但是如果您关注性能,那么您应该采用第一种方法,如果安全管理器正在运行,第二种方法也会失败.

如果我在你的情况下,我会选择第一个选项.你的第一个生成hashCode的方法也有一个错误,它应该是返回builder.toHashCode(); 而不是返回builder.hashCode();(返回hashcode构建器对象hashcode)

  • 有趣的事实:EqualsBuilder.equals()不会调用EqualsBuilder.isEquals().这不是一个坏陷阱吗?!: - / (3认同)
  • 我可以使用 hashCode() 而不是 toHashCode(),根据 HashCodeBuilder 的更新实现,hashCode() 方法现在调用 toHashCode() http://commons.apache.org/lang/api-2.6/index.html?org /apache/commons/lang/builder/HashCodeBuilder.html (2认同)

Seb*_*ino 15

即使第二个选项更具吸引力(因为它只是一行代码),我会选择第一个选项.

原因只是表现.经过一个小测试后,我发现他们之间的时间差异非常大.

为了理解时间,我创建了这两个简单的类:

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);
    }

}
Run Code Online (Sandbox Code Playgroud)

和:

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();

    };

}
Run Code Online (Sandbox Code Playgroud)

第二个类与第一个类几乎相同,但是使用不同的equals和hashCode.

之后,我创建了以下测试:

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);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

测试非常简单,只能用于测量时间.

结果如下:

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

为了拥有最美好的时光,我选择它们是完全平等的.如果您选择使用NotEquals条件进行测试,您将有更短的时间,但也要保持非常大的时差.

我使用Fedora 21和Eclipse Luna在64位Intel Core i5-3317U CPU @ 1.70GHz x4上运行此测试.

总而言之,我不会冒这么大的性能差异,以便保存几行代码,无论如何都不能使用模板输入(在Windows下的Eclipse中 - >在Java中找到首选项 - >编辑器 - >模板)如:

${: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();
}
Run Code Online (Sandbox Code Playgroud)


Pav*_*zky 8

我更喜欢第二种选择有两个原因:

  1. 显然它更容易阅读

  2. 我不会为第一个选项购买性能参数,除非这些参数包含相关指标.例如,基于反射的"等于"多少毫秒会增加典型的端到端请求延迟?总的来说,增加的百分比是多少?不知道好的机会是优化还为时过早