我很好奇,这里的人们对使用org.apache.commons.lang.builder
EqualsBuilder
/ HashCodeBuilder
实施equals
/有hashCode
什么看法
?比编写自己的方法更好吗?它与Hibernate配合使用是否很好?你怎么看?
我很好奇,这里的人们对使用org.apache.commons.lang.builder
EqualsBuilder
/ HashCodeBuilder
实施equals
/有hashCode
什么看法
?比编写自己的方法更好吗?它与Hibernate配合使用是否很好?你怎么看?
Answers:
commons / lang构建器很棒,我使用它们已有多年了,而没有明显的性能开销(有和没有休眠)。但正如Alain所写,番石榴的方法更好:
这是一个示例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;
}
}
这里是Java 7或更高版本(受Guava启发):
@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;
}
}
注意:此代码最初引用了Guava,但正如注释所指出的那样,此功能自JDK中引入以来,因此不再需要Guava。
如您所见,Guava / JDK版本较短,并且避免了多余的辅助对象。在相等的情况下,如果更早的Object.equals()
调用返回false ,它甚至可以使评估短路(公平地说:commons / lang具有ObjectUtils.equals(obj1, obj2)
相同语义的方法,可以用来代替EqualsBuilder
上述的短路)。
所以:是的,与手动构建的方法equals()
和hashCode()
方法(或Eclipse将为您生成的那些可怕的怪物)相比,公共语言生成器更可取,但是Java 7+ / Guava版本更好。
还有关于休眠的说明:
在equals(),hashCode()和toString()实现中使用惰性集合时要小心。如果您没有公开的会议,那将惨遭失败。
注意(关于equals()):
a)在上述equals()的两个版本中,您可能还希望使用以下一个或两个快捷方式:
@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){
至
// make sure you run a null check before this
if(obj.getClass() == getClass()){
如果使用第二个版本,则可能还需要super(equals())
在equals()
方法内部进行调用。意见分歧在这里,在这个问题上讨论主题:
(尽管大约是hashCode()
,这也适用于equals()
)
注意(灵感来自kayahr的评论)
Objects.hashCode(..)
Arrays.hashCode(...)
如果您有许多原始字段,则(与底层一样)的性能可能会很差。在这种情况下,EqualsBuilder
实际上可能是更好的解决方案。
equals
。番石榴将所有值转换为对象,commons-lang仅创建一个新对象。
如果您不想依赖第三方库(也许您正在运行的设备资源有限),甚至不想键入自己的方法,也可以让IDE来完成这项工作,例如在Eclipse中使用
Source -> Generate hashCode() and equals()...
您将获得“本机”代码,可以根据需要对其进行配置,并且必须在更改时提供支持。
示例(Eclipse Juno):
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
。即使只使用一两次,它也可以使代码变得更好!
equals
/ hashCode
一种方法
EqualsBuilder和HashCodeBuilder具有两个主要方面,与手动编写的代码不同:
EqualsBuilder和HashCodeBuilder使比较可能为null的字段变得更加容易。使用手动编写的代码,可以创建许多样板。
另一方面,EqualsBuilder将根据equals方法调用创建一个实例。如果经常调用equals方法,这将创建很多实例。
对于Hibernate,equals和hashCode实现没有区别。它们只是实现细节。对于几乎所有休眠状态下加载的域对象,都可以忽略Builder的运行时开销(即使没有转义分析)。数据库和通信开销将很大。
正如skaffman所提到的,反射版本不能在生产代码中使用。反思将是缓慢的,除了最简单的类,“实现”对于所有的东西都不正确。考虑到所有成员也很危险,因为新引入的成员会更改equals方法的行为。反射版本在测试代码中可能很有用。
如果您不自己写,也可以使用Google番石榴(以前称为Google收藏)
如果仅处理id为主键的实体bean,则可以简化。
@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();
}
在我看来,它与Hibernate搭配使用效果不佳,尤其是答案比较了某些实体的长度,名称和子代的示例。Hibernate建议在equals()和hashCode()中使用业务密钥,这有其原因。如果在业务密钥上使用自动equals()和hashCode()生成器,则可以,只需考虑性能问题即可,如前所述。但是人们通常使用所有属性,这是IMO非常错误的。例如,我目前正在从事使用Pojomatic和@AutoProperty编写实体的项目,我认为这是一种非常糟糕的模式。
使用hashCode()和equals()的两个主要方案是:
因此,假设我们的实体如下所示:
class 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和class / table是相等的)。但是,当我们在所有道具上实现自动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();
}
对于瞬态实体,我所做的与Hibernate在持久性步骤中所做的相同,即。我使用实例匹配。对于持久性对象,我比较了唯一键,即表/ ID(我从不使用复合键)。
为了以防万一,其他人会发现它很有用,我想出了一个用于哈希代码计算的Helper类,该类避免了上面提到的额外的对象创建开销(实际上,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
函数所吸引;表演绝对是杀手.。