Java哈希映射如何使用相同的哈希代码处理不同的对象?

Java哈希映射如何使用相同的哈希代码处理不同的对象?,java,hashmap,hashcode,hash-function,Java,Hashmap,Hashcode,Hash Function,根据我的理解,我认为: 两个对象拥有相同的哈希代码是完全合法的 如果两个对象相等(使用equals()方法),则它们具有相同的哈希代码 如果两个对象不相等,则它们不能具有相同的哈希代码 我说得对吗 如果我是对的,我有以下问题: HashMap在内部使用对象的hashcode。因此,如果两个对象可以具有相同的hashcode,那么HashMap如何跟踪它使用的键 有人能解释一下HashMap如何在内部使用对象的hashcode吗?hashcode决定HashMap要检查哪个bucket。如果buc

根据我的理解,我认为:

  • 两个对象拥有相同的哈希代码是完全合法的
  • 如果两个对象相等(使用equals()方法),则它们具有相同的哈希代码
  • 如果两个对象不相等,则它们不能具有相同的哈希代码
  • 我说得对吗

    如果我是对的,我有以下问题:
    HashMap
    在内部使用对象的hashcode。因此,如果两个对象可以具有相同的hashcode,那么
    HashMap
    如何跟踪它使用的键


    有人能解释一下HashMap如何在内部使用对象的hashcode吗?

    hashcode决定HashMap要检查哪个bucket。如果bucket中有多个对象,则会执行线性搜索以查找bucket中的哪个项目与所需项目相等(使用
    equals()
    )方法

    换句话说,如果您有一个完美的hashcode,那么hashmap访问是恒定的,您将永远不必迭代一个bucket(从技术上讲,您还必须有MAX_INT bucket,Java实现可能在同一个bucket中共享一些hash码,以减少空间需求)。如果你有最差的hashcode(总是返回相同的数字),那么你的hashmap访问就会变成线性的,因为你必须搜索map中的每一项(它们都在同一个bucket中)才能得到你想要的


    大多数情况下,编写良好的hashcode并不完美,但它的独特性足以让您或多或少地不断访问。

    您的第三个断言是不正确的

    两个不相等的对象具有相同的哈希代码是完全合法的。它被
    HashMap
    用作“第一次通过过滤器”,以便映射可以快速找到具有指定键的可能条目。然后测试具有相同哈希代码的密钥是否与指定的密钥相等


    您不希望要求两个不相等的对象不能具有相同的哈希代码,否则会将您限制为232个可能的对象。(这也意味着不同的类型甚至不能使用对象的字段来生成哈希代码,因为其他类可以生成相同的哈希。)

    第三点你错了。两个条目可以具有相同的哈希代码,但不能相等。请看一看的执行情况。您可以看到,它检查哈希值是否相等,键是否相等。如果第三点为真,则无需检查键是否相等。哈希代码在键之前进行比较,因为前者是更有效的比较

    如果您有兴趣了解更多关于这方面的内容,请参阅Wikipedia的文章,我相信这是OpenJdk实现使用的机制。这一机制与另一个答案提到的“桶”方法略有不同

    hashmap的工作原理如下(这有点简化,但它说明了基本机制):

    它有许多“bucket”,用来存储键值对。每个bucket都有一个唯一的编号,这就是bucket的标识。当您将密钥-值对放入映射时,hashmap将查看密钥的哈希代码,并将该对存储在标识符为密钥哈希代码的bucket中。例如:密钥的散列码是235->该对存储在存储桶编号235中。(请注意,一个bucket可以存储多个键值对)

    当您在hashmap中查找一个值时,通过给它一个键,它将首先查看您所给键的哈希代码。hashmap随后将查看相应的bucket,然后将您提供的密钥与bucket中所有对的密钥进行比较,方法是将它们与
    equals()
    进行比较

    现在,您可以看到这对于在映射中查找键值对是如何非常有效:通过键的哈希代码,哈希映射立即知道要在哪个bucket中查找,因此它只需根据该bucket中的内容进行测试

    查看上述机制,您还可以看到键的
    hashCode()
    equals()
    方法需要哪些要求:

    • 如果两个键相同(
      equals()
      在比较它们时返回
      true
      ),则它们的
      hashCode()
      方法必须返回相同的数字。如果键违反了这一点,那么相等的键可能存储在不同的bucket中,hashmap将无法找到键值对(因为它将在同一个bucket中查找)

    • 如果两个键不同,那么它们的哈希代码是否相同并不重要。如果它们的哈希代码相同,它们将存储在同一个bucket中,在这种情况下,hashmap将使用
      equals()
      来区分它们


    您可以在

    总结如下:

    HashMap根据哈希原理工作

    put(key,value):HashMap将key和value对象存储为Map.Entry。Hashmap应用hashcode(键)来获取bucket。若存在冲突,HashMap将使用LinkedList存储对象

    get(key):HashMap使用key对象的hashcode找出bucket位置,然后调用keys.equals()方法在LinkedList中标识正确的节点,并在Java HashMap中返回该键的关联值对象。

    HashMap
    Entry
    对象的数组

    HashMap
    视为一个对象数组

    看看这个
    对象是什么:

    static class Entry<K,V> implements Map.Entry<K,V> {
            final K key;
            V value;
            Entry<K,V> next;
            final int hash;
    … 
    }
    
    创建的阵列大小为16,默认负载平衡为0.75

    添加新的键值对
  • 计算密钥的哈希代码
  • 计算位置
    hash%(arrayLength-1)
    元素应放置的位置(桶号)
  • 如果您尝试使用已保存在
    HashMap
    中的键添加值,t
    HashMap hashMap = new HashMap();
    
    import java.util.HashMap;
    
    public class Students  {
        String name;
        int age;
    
        Students(String name, int age ){
            this.name = name;
            this.age=age;
        }
    
        @Override
        public int hashCode() {
            System.out.println("__hash__");
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            System.out.println("__eq__");
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Students other = (Students) obj;
            if (age != other.age)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
    
        public static void main(String[] args) {
    
            Students S1 = new Students("taj",22);
            Students S2 = new Students("taj",21);
    
            System.out.println(S1.hashCode());
            System.out.println(S2.hashCode());
    
            HashMap<Students,String > HM = new HashMap<Students,String > (); 
            HM.put(S1, "tajinder");
            HM.put(S2, "tajinder");
            System.out.println(HM.size());
        }
    }
    
    Output:
    
    __ hash __
    
    116232
    
    __ hash __
    
    116201
    
    __ hash __
    
    __ hash __
    
    2
    
        public static void main(String[] args) {
    
            Students S1 = new Students("taj",21);
            Students S2 = new Students("taj",21);
    
            System.out.println(S1.hashCode());
            System.out.println(S2.hashCode());
    
            HashMap<Students,String > HM = new HashMap<Students,String > (); 
            HM.put(S1, "tajinder");
            HM.put(S2, "tajinder");
            System.out.println(HM.size());
        }
    }
    
    Now lets change out main method a little bit. Output after this change is 
    
    __ hash __
    
    116201
    
    __ hash __
    
    116201
    
    __ hash __
    
    __ hash __
    
    __ eq __
    
    1
    We can clearly see that equal method is called. Here is print statement __eq__, since we have same hashcode, then content of objects MAY or MAY not be similar. So program internally  calls Equal method to verify this. 
    
    
    Conclusion 
    If hashcode is different , equal method will not get called. 
    if hashcode is same, equal method will get called.
    
    Thanks , hope it helps. 
    
    static int hashToBucket(int tableSize, int hash) {
        return (tableSize - 1) & hash;
    }
    
                int hash = hash(hashValue)
    
    {
    “girl” => “ahhan” , 
    “misused” => “Manmohan Singh” , 
    “horsemints” => “guess what”, 
    “no” => “way”
    }
    
    1) reverse the string.
    
    2) keep on multiplying ascii of each character with increasing power of 31 . then add the components .
    
    3) So hashCode() of girl would be –(ascii values of  l,r,i,g are 108, 114, 105 and 103) . 
    
    e.g. girl =  108 * 31^0  + 114 * 31^1  + 105 * 31^2 + 103 * 31^3  = 3173020
    
    Array Index 0 –
    Array Index 1 - LinkedIst (“ahhan” , “Manmohan Singh” , “guess what”)
    Array Index 2 – LinkedList (“way”)
    Array Index 3 – 
    
    Array Index 0 –
    Array Index 1 - LinkedIst (<”girl” => “ahhan”> , <” misused” => “Manmohan Singh”> , <”horsemints” => “guess what”>)
    Array Index 2 – LinkedList (<”no” => “way”>)
    Array Index 3 – 
    
    /**
         * Implements Map.get and related methods
         *
         * @param hash hash for key
         * @param key the key
         * @return the node, or null if none
         */
        final Node<K,V> getNode(int hash, Object key) {
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
            if ((tab = table) != null && (n = tab.length) > 0 &&
                (first = tab[(n - 1) & hash]) != null) {
                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;
        }
    
      /**
         * Implements Map.put and related methods
         *
         * @param hash hash for key
         * @param key the key
         * @param value the value to put
         * @param onlyIfAbsent if true, don't change existing value
         * @param evict if false, the table is in creation mode.
         * @return previous value, or null if none
         */
        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;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node<K,V> e; K k;
                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;
        }
    
    myHashmap.put("old","old-value");
    myHashMap.put("very-old","very-old-value");
    
    myHashmap.put("old","old-value");
    
    hash XOR hash >>> 16
    
    class Entry{
              final Key k;
              value v;
              final int hash;
              Entry next;
    }
    
    myHashmap.put("very-old","very-old-value");