Apache Commons等于/ hashCode构建器

aug*_*0co 155 java hibernate equals hashcode apache-commons

我很想知道,人们在这里想到使用 org.apache.commons.lang.builder EqualsBuilder/ HashCodeBuilder 实现equals/ hashCode?这比写自己更好吗?它与Hibernate相处得好吗?你怎么看?

Sea*_*oyd 212

公共/ lang建设者是伟大的,我已经使用它们多年没有明显的性能开销(有和没有休眠).但正如阿兰所写,番石榴的方式更好:

这是一个示例Bean:

public class Bean{

    private String name;
    private int length;
    private List<Bean> children;

}
Run Code Online (Sandbox Code Playgroud)

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

在这里使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:此代码最初引用了Guava,但正如评论所指出的那样,此功能已经在JDK中引入,因此不再需要Guava.

正如您所看到的,Guava/JDK版本更短,避免了多余的辅助对象.在等于的情况下,如果先前的Object.equals()调用返回false ,它甚至允许短路评估(公平地:commons/lang具有ObjectUtils.equals(obj1, obj2)可以使用相同语义的方法,而不是EqualsBuilder如上所述允许短路).

所以:是的,公共lang构建器比手动构造equals()hashCode()方法(或Eclipse将为您生成的那些可怕的怪物)更优选,但Java 7+/Guava版本甚至更好.

关于Hibernate的说明:

注意在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
Run Code Online (Sandbox Code Playgroud)

b)根据您对equals()合同的解释,您也可以更改线路

    if(obj instanceof Bean){
Run Code Online (Sandbox Code Playgroud)

    // make sure you run a null check before this
    if(obj.getClass() == getClass()){ 
Run Code Online (Sandbox Code Playgroud)

如果您使用第二个版本,您可能还想super(equals())equals()方法内部调用.意见不同,这个主题在这个问题中讨论:

将超类合并到Guava Objects.hashcode()实现的正确方法?

(虽然它是关于hashCode(),同样适用于equals())


注释(灵感来自kayahr的评论)

Objects.hashCode(..)(如同底层Arrays.hashCode(...))如果你有许多原始字段可能会表现不佳.在这种情况下,EqualsBuilder实际上可能是更好的解决方案.

  • Java 7 Objects.equals也是如此:http://download.oracle.com/javase/7/docs/api/java/util/Objects.html#equals%28java.lang.Object,%20java.lang .Object 29% (34认同)
  • @SeanPatrickFloyd Guava-way不仅为varargs创建一个数组对象,还将所有参数转换为对象.因此,当您向其传递10个int值时,您将最终得到10个Integer对象和一个数组对象.commons-lang解决方案只创建一个对象,无论您将多少个值附加到哈希代码.与`equals`相同的问题.Guava将所有值转换为对象,commons-lang仅创建一个新对象. (6认同)
  • 如果我正确地阅读它,Josh Bloch在_Effective Java_,Item 8中说,你不应该在你的equals()方法中使用getClass(); 相反,你应该使用instanceof. (3认同)

Mik*_*sov 17

伙计们,醒醒!从Java 7开始,标准库中有equalshashCode的辅助方法.它们的用法完全等同于番石榴方法的使用.


FrV*_*aBe 8

如果您不想依赖第三方库(也许您正在运行资源有限的设备)并且您甚至不想键入自己的方法,您也可以让IDE完成工作,例如在eclipse中使用

Source -> Generate hashCode() and equals()...
Run Code Online (Sandbox Code Playgroud)

您将获得"本机"代码,您可以根据需要配置这些代码以及必须支持更改的代码.


示例(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;
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 没错,但是Eclipse生成的代码是不可读的,也是不可维护的. (13认同)
  • 请永远不要想到像日食生成的"等于"那样可怕的东西.如果你不想依赖第三方库,那么自己编写像Objects.equal这样的单行方法.即使只使用一次或两次,它也会使代码更好! (6认同)

Tho*_*ung 6

EqualsBuilder和HashCodeBuilder有两个主要方面,与手动编写的代码不同:

  • 空处理
  • 实例创建

EqualsBuilder和HashCodeBuilder使得比较可能为null的字段变得更容易.使用手动编写的代码,这会创建大量的样板.

另一方面,EqualsBuilder将为每个equals方法调用创建一个实例.如果你的equals方法经常被调用,这会产生很多实例.

对于Hibernate,equals和hashCode实现没有区别.它们只是一个实现细节.对于几乎所有加载了hibernate的域对象,都可以忽略Builder的运行时开销(即使没有转义分析).数据库和通信开销将是重要的.

正如skaffman所说,反射版本不能用于生产代码.反思将是缓慢的,除了最简单的类之外,"实现"不会是正确的.考虑到所有成员也是危险的,因为新引入的成员改变了equals方法行为.反射版本在测试代码中很有用.