Object对象详解(三)之hashCode与equals

从学习Java开始,就从各个师兄、各种书籍、各大网站听到、看到,重写equals方法必须重写hashCode方法。重写equals方法必须重写hashCode方法。重写equals方法必须重写hashCode方法。

那么为什么呢?今天就详细剖析一下equals和hashCode!

equals方法是比较两个对象实例是否相等。Object中equals方法的描述为:

    public boolean equals(Object obj) {
        return (this == obj);
    }

也就是默认情况下,equals是比较两个对象的引用是否相等,这个情况下除非两个对象引用指向同一个对象,否则都会返回false。而实际场景中,我们并不需要比较两个对象的地址,而是比较它们的内同通过是否相等,这就需要我们重写equals方法。

那么是不是就简单的比较下内容是否相等呢?有什么需要注意的吗?

看下Java源码的描述:

思考这样的问题:

1. 假设两个对象的内容一样,那么它们调用equals方法会相等吗?

2. 如果在状态不改变的情况下,两次比较的结果不一样,你会不会抓狂?

3. 什么情况下返回true,什么情况下又返回false?

让我们一个一个的看,如果两个对象内容相同,但类型不一样,它们还equals吗?

/**
     * Indicates whether some other object is "equal to" this one.
     * <p>
     * The {@code equals} method implements an equivalence relation
     * on non-null object references:
     * <ul>
     * <li>It is <i>reflexive</i>: for any non-null reference value
     *     {@code x}, {@code x.equals(x)} should return
     *     {@code true}.
     * <li>It is <i>symmetric</i>: for any non-null reference values
     *     {@code x} and {@code y}, {@code x.equals(y)}
     *     should return {@code true} if and only if
     *     {@code y.equals(x)} returns {@code true}.
     * <li>It is <i>transitive</i>: for any non-null reference values
     *     {@code x}, {@code y}, and {@code z}, if
     *     {@code x.equals(y)} returns {@code true} and
     *     {@code y.equals(z)} returns {@code true}, then
     *     {@code x.equals(z)} should return {@code true}.
     * <li>It is <i>consistent</i>: for any non-null reference values
     *     {@code x} and {@code y}, multiple invocations of
     *     {@code x.equals(y)} consistently return {@code true}
     *     or consistently return {@code false}, provided no
     *     information used in {@code equals} comparisons on the
     *     objects is modified.
     * <li>For any non-null reference value {@code x},
     *     {@code x.equals(null)} should return {@code false}.
     * </ul>
     * <p>
     */
    public boolean equals(Object obj) {
        return (this == obj);
    }

SUN已经说了,equals需要遵守的规则(一下x,y均不为null):

1. 自反性(x.equals(x) 为true)

2. 一致性(x.equals(y)或y.equals(x)在没有x,y指向的对象没有改变的情况下,多次比较的结果应该是一致且明确的)

3. 传递性(x.equals(y) && y.equals(z)为true ,则x.equals(z)为true)

4. 对称性(x.equals(y) == y.equals(x) 始终为true)

下面上例子(String重写了equals):

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

那么为什么说重写equals,就必须重写hashCode呢?

源码中对hashCode方法的返回值的描述:a hash code value for this object.This method is supported for the benefit of hash tables such as those provided by java.util.HashMap.

这里提到了HashMap,再看HashMap中使用hashCode的地方

 final int hash(Object k) {
        int h = 0;
        if (useAltHashing) {
            if (k instanceof String) {
                return sun.misc.Hashing.stringHash32((String) k);
            }
            h = hashSeed;
        }

        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

从上面看到,HashMap是基于HashTable来存放元素的,在存放K-V时,会将K的hashCode作为hash函数的变量来计算K的位置,如果两个对象equals为真,但hashCode不等,那么它们将被散列到map的不同位置,最后结果就是map中存在两个K equals的对象,这就违反了Map要求Key唯一的约定。

因此,

如果重写equals,请重写hashCode。

如果重写equals,请重写hashCode。

如果重写equals,请重写hashCode。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-21 09:12:58

Object对象详解(三)之hashCode与equals的相关文章

Object对象详解(一)之toString

Object作为Java中超然的存在,其中定义了一切对象都共有的方法. 特点: 1. java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. 2. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. 3. Object类是Java中唯一没有父类的类.其他所有的类,包括标准容器类,比如数组,都继承了Object类中的方法. 下面将详细介绍Object对象的一些方法. toString方法可以返回一个字符串类型的关于该类的描述信息,我们通常会重写为我们需要的该

Object对象详解(一)之clone

clone方法会返回该实例对象的一个副本,通常情况下x.clone() != x || x.clone().getClass() == x.getClass() || x.clone().equals(x)也为真,但不严格要求,我们可以通过重写该方法来覆盖. protected native Object clone() throws CloneNotSupportedException; 可以看到,clone是一个本地方法,可能会抛出CloneNotSupportedException异常,什

JavaWeb学习(三)----JSP内置对象详解

[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4065790.html 联系方式:[email protected] [系列]JSP学习系列文章:(持续更新) JavaWeb学习(一)----JSP简介及入门(含Tomcat的使用) JavaWeb学习(二)----JSP脚本元素.指令元素.动作元素 JavaWeb学习(三)----JSP内置对象

JavaScript学习总结(十一)——Object类详解

一.Object类介绍 Object类是所有JavaScript类的基类(父类),提供了一种创建自定义对象的简单方式,不再需要程序员定义构造函数. 二.Object类主要属性 1.constructor:对象的构造函数. 2.prototype:获得类的prototype对象,static性质. 三.Object类主要方法 1.hasOwnProperty(propertyName) 判断对象是否有某个特定的属性.必须用字符串指定该属性,例如,obj.hasOwnProperty("name&q

java对象详解

java对象详解 内存布局 普通对象布局 数组的内存布局 内部类的内存布局 对象分解 对象头-mark word(8字节) 实例数据 对齐填充(可选) java锁分析 java对象详解 HotSpot虚拟机中,对象在内存中存储的布局可以分为对象头,实例数据,对齐填充三个区域.本文所说环境均为HotSpot虚拟机.即输入java -version返回的虚拟机版本: java version "1.8.0_111" Java(TM) SE Runtime Environment (buil

JavaScript 对象详解

1 创建对象的方法 最常见的创建对象方法是通过new调用构造函数,此外还有一个方法就是使用Object.create(),将一个原型对象作为参数传递给Object.create也可以创建一个继承了其属性的新对象.但是和使用new创建对象还有一点差别,那就是构造函数不会被调用.所以如果使用这种方法创建一个foo新对象,其foo.f是undefined的: function Foo(z) { this.f = z; } Foo.prototype.add = function(x, y) { ret

JS的window对象详解

一.说明 他是JS中最大的对象,它描述的是一个浏览器窗口,一般要引用他的属性和方法时,不需要用"Window.XXX"这种形式,而是直接使用"XXX".一个框架页面也是一个窗口. 二.Window窗口对象有如下属性 1.name 窗口的名称,由打开它的连接(<a target="...">)或框架页(<frame name="...">)或某一个窗口调用的 open() 方法(见下)决定.一般我们不会用

【转载】html中object标签详解

[转载自http://blog.csdn.net/soliy/archive/2010/03/22/5404183.aspx] html标签之Object标签详解 作者:网络    出处:网络    2010年3月22日13:36:29 定义和用法定义一个嵌入的对象.请使用此元素向您的 XHTML 页面添加多媒体.此元素允许您规定插入 HTML 文档中的对象的数据和参数,以及可用来显示和操作数据的代码.<object> 标签用于包含对象,比如图像.音频.视频.Java applets.Acti

mvc-servlet---ServletConfig与ServletContext对象详解(转载)

ServletConfig与ServletContext对象详解 一.ServletConfig对象    在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数.(配置在某个servlet标签或者整个web-app下) 当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在 调用servlet的init方法时,将ServletConfig对象传递给