Java HashCodeBuilder和EqualBuilder使用风格
我经常使用apachehashcodebuilder和EqualsBuilder使用反射实现对象相等,但最近我的一位同事告诉我,如果实体包含大量属性,使用反射可能会导致巨大的性能损失。我担心我可能使用了错误的实现,我的问题是,您更喜欢以下哪种方法?为什么Java HashCodeBuilder和EqualBuilder使用风格,java,hibernate,apache-commons,Java,Hibernate,Apache Commons,我经常使用apachehashcodebuilder和EqualsBuilder使用反射实现对象相等,但最近我的一位同事告诉我,如果实体包含大量属性,使用反射可能会导致巨大的性能损失。我担心我可能使用了错误的实现,我的问题是,您更喜欢以下哪种方法?为什么 public class Admin { private Long id; private String userName; public String getUserName() { return
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
当然,第二种选择更加优雅和简单。但是,如果您关心性能,您应该采用第一种方法。如果安全管理器正在运行,第二种方法也会失败。 如果我处在你的处境,我会选择第一种选择 另外,在生成hashCode的第一种方法中有一个错误:
它应该是
builder.toHashCode()
而不是
builder.hashCode()
。后者返回hashcode builder对象的哈希代码。出于两个原因,我更喜欢第二个选项:
您的书面问题清楚地说明了第二种方法的好处之一: 在第一种情况下,很容易出现错误
return builder.hashCode()
,而不是正确的return builder.toHashCode()
,这将导致很难跟踪的细微错误
第二种情况消除了这种输入错误的可能性,从而减少了在试图查找错误时头部撞击键盘的次数。我认为这两种情况都不是很好的实现。我认为EqualsBuilder不是一个好的框架,原因如下:
我要说的是,需要有一个比Apache更好的框架。即使第二个选项更吸引人(因为它只是一行代码),我还是会选择第一个选项 原因很简单,就是性能。在运行了一个小测试之后,我发现它们之间的时间差很大 为了了解时间,我创建了两个简单的类:
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);
}
}
以及:
第二个类与第一个类几乎相同,但具有不同的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);
}
}
}
同意@Churk,Apache HashCodeBuilder和EqualBuilder没有很好地实现。HashCodeBuilder仍然在玩素数!此外,它还做了大量不必要的工作。你读过消息来源吗 自Java5(如果不是更早的话)以来,AbstractHashMap没有使用质数的模来定位哈希桶。相反,bucket的数量是2的幂,哈希代码的低阶N位用于定位bucket 此外,它将“混合”应用程序提供的散列码,以使位均匀分布,从而使存储桶均匀填充 因此,覆盖int Object.hashCode()的正确方法是返回将在使用该类的任何集合中共存的对象的总体中具有最高算术性的最简单常量值 通常,未修改的ID值是最佳选择。如果ID字段是整数,只需将其转换为(int)并返回它。如果是字符串或其他对象,只需返回其哈希代码。你明白了。对于复合标识符,返回具有最不同值的字段(或其哈希代码)。少就是多
当然,必须满足hashCode()和equals()之间的约定。因此,应该相应地实现equals()。hashCode()不需要使用相等所需的完整限定符,但hashCode()中使用的任何字段都必须在equals()中使用。在这里,像StringUtils.equals(s1,s2)这样的方法对于一致且安全地处理空值非常有用。我可以使用hashCode()而不是toHashCode(),根据HashCodeBuilder的更新实现,hashCode()方法现在调用hashCode()有趣的事实:EqualBuilder.equals()不调用equals Builder.isEquals()。这不是个坏陷阱吗-/@彼得·维伯曼:不太可能-
EqualsBuilder.equals()
和EqualsBuilder.isEquals()
有完全不同的签名,所以不容易混淆-你不能调用EqualsBuilder.equals()
来代替EqualsBuilder.isEquals()
。与HashCodeBuilder.hashCode()
和HashCodeBuilder.toHashCode()
不同的是,方法签名完全相同。您需要接受下面适合您的问题的答案之一。我也有同样的问题,无法从下面的答案中证明哪一个是正确的。试着接受任何一个答案。为什么他需要接受下面的一个答案?如果问题没有得到op满意的回答,为什么要接受。我会说,在有人给出一个很好的答案之前,让它保持打开状态。更重要的是:HashCodeBuilder.reflectionHashCode()和EqualBuilder.ReflectionQuals()实际上有些危险,因为它们很容易违反equals()/hashCode()契约的“一致”部分。发生的情况如下:如果一个对象被放置在某种哈希表/集合中,那么
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();
};
}
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);
}
}
}
${: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();
}