org.apache.commons.lang.builder
EqualsBuilder
/HashCodeBuilder
来实现equals
/hashCode
有什么看法?相比编写自己的代码,这种做法是否更好?它是否与Hibernate兼容?你有什么意见?org.apache.commons.lang.builder
EqualsBuilder
/HashCodeBuilder
来实现equals
/hashCode
有什么看法?相比编写自己的代码,这种做法是否更好?它是否与Hibernate兼容?你有什么意见?commons/lang的构建工具非常棒,我已经使用了几年而没有注意到性能开销(使用hibernate和不使用hibernate都一样)。但正如Alain所说,Guava的方式更加优雅:
这是一个示例Bean:
public class Bean{
private String name;
private int length;
private List<Bean> children;
}
以下是使用Commons/Lang实现的equals()和hashCode()方法:
@Override
public int hashCode(){
return new HashCodeBuilder()
.append(name)
.append(length)
.append(children)
.toHashCode();
}
@Override
public boolean equals(final Object obj){
if(obj instanceof Bean){
final Bean other = (Bean) obj;
return new EqualsBuilder()
.append(name, other.name)
.append(length, other.length)
.append(children, other.children)
.isEquals();
} else{
return false;
}
}
@Override
public int hashCode(){
return Objects.hash(name, length, children);
}
@Override
public boolean equals(final Object obj){
if(obj instanceof Bean){
final Bean other = (Bean) obj;
return Objects.equals(name, other.name)
&& length == other.length // special handling for primitives
&& Objects.equals(children, other.children);
} else{
return false;
}
}
@Override
public boolean equals(final Object obj){
if(obj == this) return true; // test for reference equality
if(obj == null) return false; // test for null
// continue as above
b) 根据您对 equals() 合同的解释,您可能还需要更改该行或几行代码
if(obj instanceof Bean){
to
// make sure you run a null check before this
if(obj.getClass() == getClass()){
equals()
方法内调用super(equals())
。在这里,意见不一,该主题在以下问题中讨论:right way to incorporate superclass into a Guava Objects.hashcode() implementation?(虽然它是关于hashCode()
,但同样适用于equals()
)。
Objects.hashCode(..)
(以及底层的Arrays.hashCode(...)
)可能表现不佳。在这种情况下,EqualsBuilder
实际上可能是更好的解决方案。equals()
方法时应该避免使用 getClass()
方法,而应该使用 instanceof
进行判断。 - Jeff Olson如果您不想依赖第三方库(也许您正在运行资源有限的设备),甚至不想自己编写方法,您也可以让 IDE 来完成这项工作,例如在 Eclipse 中使用:
Source -> Generate hashCode() and equals()...
import java.util.Arrays;
import java.util.List;
public class FooBar {
public String string;
public List<String> stringList;
public String[] stringArray;
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((string == null) ? 0 : string.hashCode());
result = prime * result + Arrays.hashCode(stringArray);
result = prime * result
+ ((stringList == null) ? 0 : stringList.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
FooBar other = (FooBar) obj;
if (string == null) {
if (other.string != null)
return false;
} else if (!string.equals(other.string))
return false;
if (!Arrays.equals(stringArray, other.stringArray))
return false;
if (stringList == null) {
if (other.stringList != null)
return false;
} else if (!stringList.equals(other.stringList))
return false;
return true;
}
}
equals
。如果您不想依赖第三方库,那么可以自己写一个像Objects.equal
这样的一行代码方法。即使只使用一两次,它也能使代码更好! - maaartinusclass Entity {
protected Long id;
protected String someProp;
public Entity(Long id, String someProp);
}
Entity entity1 = new Entity(1, "a");
Entity entity2 = new Entity(1, "b");
对于Hibernate来说,它们都是相同的实体,在某个时刻从某个会话中获取(它们的id和类/表相等)。但是当我们在所有属性上实现自动equals()和hashCode()时,我们得到了什么呢?
因此,对于99%的项目,我们使用以下在基本实体类中编写的equals()和hashCode()的实现,该实现与Hibernate概念一致:
@Override
public boolean equals(Object obj) {
if (StringUtils.isEmpty(id))
return super.equals(obj);
return getClass().isInstance(obj) && id.equals(((IDomain) obj).getId());
}
@Override
public int hashCode() {
return StringUtils.isEmpty(id)
? super.hashCode()
: String.format("%s/%s", getClass().getSimpleName(), getId()).hashCode();
}
如果你只是处理实体bean,其中id是主键,你可以简化操作。
@Override
public boolean equals(Object other)
{
if (this == other) { return true; }
if ((other == null) || (other.getClass() != this.getClass())) { return false; }
EntityBean castOther = (EntityBean) other;
return new EqualsBuilder().append(this.getId(), castOther.getId()).isEquals();
}
以防万一,其他人可能会发现这个有用,我为哈希码计算设计了这个辅助类,它避免了上述额外的对象创建开销(事实上,当你有继承时,Objects.hash()方法的开销甚至更大,因为它会在每个级别上创建一个新数组!)。
使用示例:
public int hashCode() {
return HashCode.hash(HashCode.hash(timestampMillis), name, dateOfBirth); // timestampMillis is long
}
public int hashCode() {
return HashCode.hash(super.hashCode(), occupation, children);
}
HashCode助手:
public class HashCode {
public static int hash(Object o1, Object o2) {
return add(Objects.hashCode(o1), o2);
}
public static int hash(Object o1, Object o2, Object o3) {
return hash(Objects.hashCode(o1), o2, o3);
}
...
public static int hash(Object o1, Object o2, ..., Object o10) {
return hash(Objects.hashCode(o1), o2, o3, ..., o10);
}
public static int hash(int initial, Object o1, Object o2) {
return add(add(initial, o1), o2);
}
...
public static int hash(int initial, Object o1, Object o2, ... Object o10) {
return add(... add(add(add(initial, o1), o2), o3) ..., o10);
}
public static int hash(long value) {
return (int) (value ^ (value >>> 32));
}
public static int hash(int initial, long value) {
return add(initial, hash(value));
}
private static int add(int accumulator, Object o) {
return 31 * accumulator + Objects.hashCode(o);
}
}
我已经发现,在一个领域模型中,10是最大合理的属性数量。如果你有更多的属性,你应该考虑重构并引入更多的类,而不是维护一堆字符串和基本类型。
缺点是:如果你主要有原始类型和/或需要深度哈希的数组,它就没有用了。(通常情况下,当你必须处理超出你控制范围的平面(传输)对象时,就会出现这种情况)。
reflectionEquals
和reflectionHashcode
函数所诱惑,它们会极大地影响性能。 - skaffman