hashCode()方法与equals()方法的联系

1.功能

1.1 hashCode()

hashCode() 是 Java 中的一个方法,它用于计算对象的哈希码(hash code)。每个 Java 对象都可以调用 hashCode() 方法来获取其哈希码。

哈希码是一个整数值,用于快速确定对象是否相等。在 Java 中,哈希码通常用于优化数据结构和算法,例如散列集合(如 HashSet 和 HashMap)。
以下是 hashCode() 方法的常见特性:

  • 一致性:如果对象的属性没有发生变化,多次调用 hashCode() 方法应该返回相同的值。但注意,当对象的属性改变时,其哈希码可能会改变。

  • 相等性:根据 equals() 方法来定义的相等对象应具有相等的哈希码。也就是说,如果两个对象使用 equals() 判断为相等,则它们的哈希码必须相等。

  • 效率:计算哈希码的算法应尽可能高效,以避免性能问题。通常情况下,良好的哈希算法应将不同的对象映射到不同的哈希码,以减少哈希冲突的可能性。

需要注意的是,重写 hashCode() 方法时,还应同时重写 equals() 方法,以确保二者的行为保持一致。
String.java

public int hashCode() {
        // The hash or hashIsZero fields are subject to a benign data race,
        // making it crucial to ensure that any observable result of the
        // calculation in this method stays correct under any possible read of
        // these fields. Necessary restrictions to allow this to be correct
        // without explicit memory fences or similar concurrency primitives is
        // that we can ever only write to one of these two fields for a given
        // String instance, and that the computation is idempotent and derived
        // from immutable state
        int h = hash;
        if (h == 0 && !hashIsZero) {
            h = isLatin1() ? StringLatin1.hashCode(value)
                           : StringUTF16.hashCode(value);
            if (h == 0) {
                hashIsZero = true;
            } else {
                hash = h;
            }
        }
        return h;
    }

StringLatin1.java

    public static int hashCode(byte[] value) {
        int h = 0;
        for (byte v : value) {
            h = 31 * h + (v & 0xff);
        }
        return h;
    }

1.2 equals()

==比较的是对象的引用,equals()只可被对象调用,在Object类中「equals()方法是通过“==”号比较两个实例变量是否指向同一个对象」,但是在派生类中一般会被重写,比较对象的内容是否相同;
String.java

    @IntrinsicCandidate
    public static boolean equals(byte[] value, byte[] other) {
        if (value.length == other.length) {
            for (int i = 0; i < value.length; i++) {
                if (value[i] != other[i]) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

2. 二者联系

2.1 不用于哈希表

当自定义的类对象不用于【哈希表(hash)】时,不需要重写hashCode(),直接继承Object类的方法(在Object类中,「hashCode()方法」是一个「native本地方法」,返回的是“对象对应的十进制的内存地址”。),可以根据需要重写equals()即可

2.2 用于【哈希表】

用于【哈希表】且自定义类对象用作key时,需要重写hashCode()方法和equals()方法。
一定要重写原因:

  1. 既然equals()方法能够完全确定元素是否重复,那为什么还要加入hashCode()方法比较hashCode值呢?---- 因为重写的equals()方法中一般比较全面和复杂,导致效率比较低,而利用hashCode()进行对比,只需要生成一个hash Code值进行比较就可以了,效率很高。
  2. 那么既然hashCode()效率这么高为什么还要用equal()呢?---- 因为hashCode()并不是完全可靠的,有时候2对象的hashCode值相同,并不一定是重复的,也有可能是产生了“哈希冲突”,这个时候就需要使用equal()方法来判断“它们到底是重复元素,还是产生了‘哈希冲突’”(equal()返回true,则表示是重复元素;equals()返回false,则表示是产生了‘哈希冲突’)
    一句话总结:先比较对象的hash值,如果hash值相等再equals()否则直接跳过。

源码分析: put方法返回值:previous value, or null if none

2.2.1 put方法

HashMap.java

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

static final int hash(Object key) {
        int h;
        //调用key的hashCode方法获得hash值
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        // 根据hash值获取该对象对应位置在tab中的值
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            // 如果该位置存在值,hash值相等、对象引用一致或者值相等
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            //更新值
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

2.2.2get方法

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(key)) == null ? null : e.value;
    }
final Node<K,V> getNode(Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
        	//获取key的hash值并且`(n - 1) & (hash = hash(key))`取得该位置的值
            (first = tab[(n - 1) & (hash = hash(key))]) != null) {
            // 比较对应对象的hash值是否相等,相等则继续比较key应用是否一致或者内容是否一致,返回节点
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值