zoukankan      html  css  js  c++  java
  • Java的==与equals之辨

    =======================原理速记===================================

    equals重载后比较内容,==比较指针。否则equls等同于== (Java里源码如此)

    所有的“==”都是基于指针级的,也就是说只有它们指向的是同一个对象才被认为是相等.
    而对于对象值的相等判断则需要重写并使用.equal()方法
    在多数常用类,譬如String中,.equal()已经被默认重写为是值相等的了


    两个对象用==号比较,比较的就是 两个对象的引用。
    而equals方法比较的是内容。比较String就覆盖了equals方法(自己重定义逻辑相等)

    因为内容相等,所以始终输出equals的效果,而==却不一定,例子:
    http://www.cnblogs.com/zhxhdean/archive/2011/03/25/1995431.html

    ================================equals哲学============================

    1. 何时需要重写equals()
    当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念)。

    2. 设计equals()
    [1]使用instanceof操作符检查“实参是否为正确的类型”。
    [2]对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。
    [2.1]对于非float和double类型的原语类型域,使用==比较;
    [2.2]对于对象引用域,递归调用equals方法;
    [2.3]对于float域,使用Float.floatToIntBits(afloat)转换为int,再使用==比较;
    [2.4]对于double域,使用Double.doubleToLongBits(adouble) 转换为int,再使用==比较;
    [2.5]对于数组域,调用Arrays.equals方法。

    3. 当改写equals()的时候,总是要改写hashCode()
    根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode方法,它们仅仅是两个对象。因此,违反了“相等的对象必须具有相等的散列码”。

    4. hashCode最大的用处是什么呢? 

    Java中的集合(Collection)有两类,一类是List,再有一类是Set。 前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。
    那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢? 这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。Java采用了哈希表的原理, 哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上. 初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。 有了hashCode,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。 如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。 所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

    5. 为什么在Hibernate里要重写hashCode和equals这两个方法?
    在hibernate中,经常使用set集合来保存相关对象,而set集合是不允许重复的, so, 道理同上。

    ===========================一道写HashCode的题===========================

    一道java hashCode()的面试题
    最近面试老是被问到这个问题。。。

    public class ValuePair {
        public int a = 4, b;
    
        public boolean equals(Object other) {
            try {
                ValuePair o = (ValuePair) other;
                return ((a == o.a) && (b == o.b)) || ((a == o.b) && (b == o.a));
            } catch (ClassCastException cce) {
                return false;
            }
        }
    
        public int hashCode() {
            // 请选择下边的答案(多选)
        }
    }

    A。return 0;
    B. return a;
    C. return a+b;
    D. return a-b;
    E. return a^b;
    F. return (a<<16)|b;
    请说明理由。

    ------------------------------------第一种解答----------------------------------------------------------------------
    hashCode 的常规协定是:
    在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
    如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
    如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
    实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)


    然后再看重写的equals方法。。
    (a==o.a;;b==o.b) || (a==o.b;;b==o.a); 说明交换a,b的值不会对结果产生影响,也要生成相同的hashcode值。

    所以选ACE。。。(Cliff评注:A还是不能选)

    ------------------------------------第二种解答----------------------------------------------------------------------

    根据一般约定:
    如果2个对象 equals 比较为true,那么hashCode也最好(不是必须)相同。(Cliff:我也比较同意,这是最好,但不是必须)
    如果2个对象 equals 比如为false,那么hashCode也最好(不是必须)不同。

    因此对于这个题目,如果从纯语法层面考虑,全部都是可选的
    但是从程序运行效率来看,最好是该hashCode里,a,b值可以互换,而又尽可能做到不同的a,b值返回不同的值。因为C,E最好.

    A. return 0; 效率太低
    B. return a; 不满足a,b的互换性
    D. return a-b; 不满足a,b的互换性
    F. return (a<<16)|b; 不满足a,b的互换性

    另外,比如 a * b 也满足互换性,但是效率稍低,毕竟没加法和位运算快


    * Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
    * If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
    * It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

    ===========================另一个例子,用来熟悉语法===========================

    package dd;
    
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
    
    public class SetTest {
    
        public static void main(String[] args) {
            // Set<String> set = new HashSet<String>();
            // set.add("abc"); //将对象的地址增加到集合中了
            // set.add("xyz");
            // set.add("abc");
            //
            // for(Iterator<String> iter = set.iterator(); iter.hasNext();) {
            //     System.out.println(iter.next());
            // }
    
            Set<People> set = new HashSet<People>();
            set.add(new People("zhangfei"));
            set.add(new People("guanyu"));
            for (Iterator<People> iter = set.iterator(); iter.hasNext();) {
                System.out.println(iter.next().getName());
            }
    
            // Object object = new Object();
            // System.out.println(Integer.toHexString(object.hashCode()));
        }
    }
    
    class People {
        private String name;
    
        public People(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public boolean equals(Object object) {
            if (this == object) { // 第一步
                return true;
            }
            if (object instanceof People) { // 第二步
                People people = (People) object;
                if (name.equals(people.getName())) { // 第三步,这里很巧妙的使用了String的比较,属于递归调用
                    return true;
                }
            }
            return false;
        }
    
        public int hashCode() {
            return name.hashCode();
        }
    }

    三、小结
    1,如果要用自己的类作为HashMap的key,必须同时覆盖hashCode和equals方法。
    2,重写hashCode方法的关键:
    (1)对同一个对象调用hashCode()都应该生成同样的值。
    (2)hashCode()方法不要依赖于对象中易变的数据,当数据发生变化时,hashCode()就会生成一个不同的散列码,即产生了一个不同的label。
    (3)hashCode()不应依赖于具有唯一性的对象信息,例如对象地址。
    (4)散列码应该更关心速度,而不是唯一性,因为散列码不必是唯一的。
    (5)好的hashCode()应该产生分步均匀的散列码。
    参考:
    http://blog.csdn.net/lazy_tiger/article/details/1816946

  • 相关阅读:
    js获取上一个兄弟元素
    js验证身份证
    github绑定自己的域名
    vue子组件传参给父组件
    vue父组件传参给子组件
    运行vue init webpack vueTest时报错
    运用CSS高斯模糊添加图片加载效果
    分享记录一批免费VIP视频解析接口,不定时更新!
    收藏的一些有意思的CSS加载样式
    一款很好用的页面滚动元素动画插件-AOS.JS
  • 原文地址:https://www.cnblogs.com/findumars/p/3240761.html
Copyright © 2011-2022 走看看