• How to implement equals() and hashCode() methods in Java[reproduced]


    Part I:
    equals() (javadoc) must define an equivalence relation (it must be reflexive, symmetric, and transitive). In addition, it must be consistent (if the objects are not modified, then it must keep returning the same value). Furthermore, o.equals(null) must always return false.

    hashCode() (javadoc) must also be consistent (if the object is not modified in terms of equals(), it must keep returning the same value).

    The relation between the two methods is:

    Whenever a.equals(b), then a.hashCode() must be same as b.hashCode().
    In practice:

    If you override one, then you should override the other.

    Use the same set of fields that you use to compute equals() to compute hashCode().

    Use the excellent helper classes EqualsBuilder and HashCodeBuilder from the Apache Commons Lang library. An example:

    public class Person {
        private String name;
        private int age;
        // ...
    
        @Override
        public int hashCode() {
            return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
                // if deriving: appendSuper(super.hashCode()).
                append(name).
                append(age).
                toHashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
           if (!(obj instanceof Person))
                return false;
            if (obj == this)
                return true;
    
            Person rhs = (Person) obj;
            return new EqualsBuilder().
                // if deriving: appendSuper(super.equals(obj)).
                append(name, rhs.name).
                append(age, rhs.age).
                isEquals();
        }
    }

    Also remember:
    When using a hash-based Collection or Map such as HashSet, LinkedHashSet, HashMap, Hashtable, or WeakHashMap, make sure that the hashCode() of the key objects that you put into the collection never changes while the object is in the collection. The bulletproof way to ensure this is to make your keys immutable, which has also other benefits.

    PartII:

    The commons/lang builders are great and I have been using them for years without noticeable performance overhead (with and without hibernate). But as Alain writes, the Guava way is even nicer:

    Here's a sample Bean:

    public class Bean{
    
        private String name;
        private int length;
        private List<Bean> children;
    
    }
    Here's equals() and hashCode() implemented with Commons/Lang:
    
    @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;
        }
    }
    and here with Guava:
    
    @Override
    public int hashCode(){
        return Objects.hashCode(name, length, children);
    }
    
    @Override
    public boolean equals(final Object obj){
        if(obj instanceof Bean){
            final Bean other = (Bean) obj;
            return Objects.equal(name, other.name)
                && length == other.length // special handling for primitives
                && Objects.equal(children, other.children);
        } else{
            return false;
        }
    }

    As you can see the Guava version is shorter and avoids superfluous helper objects. In case of equals, it even allows for short-circuiting the evaluation if an earlier Object.equal() call returns false (to be fair: commons / lang has an ObjectUtils.equals(obj1, obj2) method with identical semantics which could be used instead of EqualsBuilder to allow short-circuiting as above).

    So: yes, the commons lang builders are very preferable over manually constructed equals() and hashCode() methods (or those awful monsters Eclipse will generate for you), but the Guava versions are even better.

    And a note about Hibernate:

    be careful about using lazy collections in your equals(), hashCode() and toString() implementations. That will fail miserably if you don't have an open Session.

    Note (about equals()):

    a) in both versions of equals() above, you might want to use one or both of these shortcuts also:

    @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) depending on your interpretation of the equals() contract, you might also change the line(s)

    if(obj instanceof Bean){
    to

    // make sure you run a null check before this
    if(obj.getClass() == getClass()){
    If you use the second version, you probably also want to call super(equals()) inside your equals() method. Opinions differ here, the topic is discussed in this question:

    right way to incorporate superclass into a Guava Objects.hashcode() implementation?
    (although it's about hashCode(), the same applies to equals())

    Note (inspired by Comment from kayahr)

    Objects.hashCode(..) (just as the underlying Arrays.hashCode(...)) might perform badly if you have many primitive fields. In such cases, EqualsBuilder may actually be the better solution.

  • 相关阅读:
    SVD与PCA
    Service(二):通信
    Service(一):认识service、绑定Service
    计划(四)
    Android studio 安装过程中遇到的问题
    UFLDL 教程学习笔记(四)
    opencv之dft及mat类型转换
    《第一行代码》(四)
    《第一行代码》
    计划(三)
  • 原文地址:https://www.cnblogs.com/pugang/p/5801093.html
Copyright © 2020-2023  润新知