zoukankan      html  css  js  c++  java
  • HashMap解读

    hashcode()方法和equals()方法。使用这两个方法,一个对象能够存储或从一个Hashtable,HashMap,HashSet 被检索。

    hashcode():

    This method is used to get unique integer for given object. This integer is used to find bucket when storing in hashmap or hashset.By default, This method returns integer represention of memory address where object is stored.
     
    equals():
    This method is used to simply check the equality between two objects. By default, it checks where two reference refer to same object or not(==).
     
    当我们有需要时,可以通过重写这两个方法来达到目的。
     
    例子:
    public class Country {  
      
        String name;  
        long population;  
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        public long getPopulation() {  
            return population;  
        }  
        public void setPopulation(long population) {  
            this.population = population;  
        }  
          
    }  

    重写equals()方法   1:对象是否和当前对象为同一个对象;2.对象是否为空;3.对象所属类和当前对象所属类是否一样;4.具体属性的比较   注意:方法签名的书写

     public boolean equals(Object obj) {  
            if (this == obj)  
                return true;  
            if (obj == null)  
                return false;  
            if (getClass() != obj.getClass())  
                return false;  
            Country other = (Country) obj;  
            if (name == null) {  
                if (other.name != null)  
                    return false;  
            } else if (!name.equals(other.name))  
                return false;  
            return true;  
        }  

    put this Country objects in hashmap

    package org.arpit.java2blog;  
      
    import java.util.HashMap;  
    import java.util.Iterator;  
      
    public class HashMapEqualityCheckMain {  
      
        /** 
         * @author Arpit Mandliya 
         */  
        public static void main(String[] args) {  
            HashMap<Country,String> countryCapitalMap=new HashMap<Country,String>();   
            Country india1=new Country();  
            india1.setName("India");  
            Country india2=new Country();  
            india2.setName("India");  
      
            countryCapitalMap.put(india1, "Delhi");  
            countryCapitalMap.put(india2, "Delhi");  
      
            Iterator<Country> countryCapitalIter=countryCapitalMap.keySet().iterator();  
            while(countryCapitalIter.hasNext())  
            {  
                Country countryObj=countryCapitalIter.next();  
                String capital=countryCapitalMap.get(countryObj);  
                System.out.println("Capital of "+ countryObj.getName()+"----"+capital);  
      
            }  
        }   
    } 

    结果:

    Capital of India----Delhi  
    Capital of India----Delhi  

    HashMap uses hashcode to find bucket for that key object, if hashcodes are same then only it checks for equals method and because hashcode for above two country objects uses default hashcode method,Both will have different memory address hence different hashcode.

    override hashcode method

     public int hashCode() {  
            final int prime = 31;  
            int result = 1;  
            result = prime * result + ((name == null) ? 0 : name.hashCode());  
            return result;  
        }  

    结果:

    Capital of India----Delhi

    now hashcode for above two objects india1 and india2 are same, so Both will be point to same bucket,now equals method will be used to compare them which  will return true.

    if you override equals() method then you must override hashCode() method

    总结:在hashmap比较key时,key对象(比如String Country等)的hashcode值相等时,再进行key对象的equals()方法的比较,若equals()相等,才能说明两个key一样。那么hashmap存的只有一个而不是两个。

    扩展:

    equals方法的四个特性

     如果两个对象相等,那么他们必须含有相同的hashcode

    如果两个对象的hashcode相等,他们的equals可能相等也可能不相等

    HashMap----一个叫做table大小是16的Entry数组。

    这个table数组存储了Entry类的对象。HashMap类有一个叫做Entry的内部类这个Entry类包含了key-value作为实例变量。我们来看下Entry类的结构。Entry类的结构:

    static class Entry implements Map.Entry
    {
            final K key;
            V value;
            Entry next;
            final int hash;
            ...//More code goes here
    }   
    1. 每当往hashmap里面存放key-value对的时候,都会为它们实例化一个Entry对象,这个Entry对象就会存储在前面提到的Entry数组table中。现在你一定很想知道,上面创建的Entry对象将会存放在具体哪个位置(在table中的精确位置)。答案就是,根据key的hashcode()方法计算出来的hash值(来决定)。hash值用来计算key在Entry数组的索引

    2. 我们往hashmap放了4个key-value对,但是看上去好像只有2个元素!!!这是因为,如果两个元素有相同的hashcode,它们会被放在同一个索引上。问题出现了,该怎么放呢?原来它是以链表(LinkedList)的形式来存储的(逻辑上)。

    put方法的实现:

    /**
      * Associates the specified value with the specified key in this map. If the
      * map previously contained a mapping for the key, the old value is
      * replaced.
      *
      * @param key
      *            key with which the specified value is to be associated
      * @param value
      *            value to be associated with the specified key
      * @return the previous value associated with <tt>key</tt>, or <tt>null</tt>
      *         if there was no mapping for <tt>key</tt>. (A <tt>null</tt> return
      *         can also indicate that the map previously associated
      *         <tt>null</tt> with <tt>key</tt>.)
      */
     public V put(K key, V value) {
      if (key == null)
       return putForNullKey(value);
      int hash = hash(key.hashCode());
      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;
     }
    1. 对key做null检查。如果key是null,会被存储到table[0],因为null的hash值总是0

    2. key的hashcode()方法会被调用,然后计算hash值。hash值用来找到存储Entry对象的数组的索引。有时候hash函数可能写的很不好,所以JDK的设计者添加了另一个叫做hash()的方法,它接收刚才计算的hash值作为参数。
    3. indexFor(hash,table.length)用来计算在table数组中存储Entry对象的精确的索引。

    4. 在我们的例子中已经看到,如果两个key有相同的hash值(也叫冲突),他们会以链表的形式来存储。所以,这里我们就迭代链表
    • 如果在刚才计算出来的索引位置没有元素,直接把Entry对象放在那个索引上。
    • 如果索引上有元素,然后会进行迭代,一直到Entry->next是null。当前的Entry对象变成链表的下一个节点。
    • 如果我们再次放入同样的key会怎样呢?逻辑上,它应该替换老的value。事实上,它确实是这么做的。在迭代的过程中,会调用equals()方法来检查key的相等性(key.equals(k)),如果这个方法返回true,它就会用当前Entry的value来替换之前的value。

    get方法:

    /**
      * Returns the value to which the specified key is mapped, or {@code null}
      * if this map contains no mapping for the key.
      *
      * <p>
      * More formally, if this map contains a mapping from a key {@code k} to a
      * value {@code v} such that {@code (key==null ? k==null :
      * key.equals(k))}, then this method returns {@code v}; otherwise it returns
      * {@code null}. (There can be at most one such mapping.)
      *
      * </p><p>
      * A return value of {@code null} does not <i>necessarily</i> indicate that
      * the map contains no mapping for the key; it's also possible that the map
      * explicitly maps the key to {@code null}. The {@link #containsKey
      * containsKey} operation may be used to distinguish these two cases.
      *
      * @see #put(Object, Object)
      */
     public V get(Object key) {
      if (key == null)
       return getForNullKey();
      int hash = hash(key.hashCode());
      for (Entry<k , V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
       Object k;
       if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
        return e.value;
      }
      return null;
     }
    1. 对key进行null检查。如果key是null,table[0]这个位置的元素将被返回。

    2. key的hashcode()方法被调用,然后计算hash值。

    3. indexFor(hash,table.length)用来计算要获取的Entry对象在table数组中的精确的位置,使用刚才计算的hash值。

    4. 在获取了table数组的索引之后,会迭代链表,调用equals()方法检查key的相等性,如果equals()方法返回true,get方法返回Entry对象的value,否则,返回null。

    要牢记以下关键点:

    • HashMap有一个叫做Entry的内部类,它用来存储key-value对。
    • 上面的Entry对象是存储在一个叫做table的Entry数组中。
    • table的索引在逻辑上叫做“桶”(bucket)它存储了链表的第一个元素。
    • key的hashcode()方法用来找到Entry对象所在的
    • 如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。
    • key的equals()方法用来确保key的唯一性
    • value对象的equals()和hashcode()方法根本一点用也没有。

    参考文献:http://www.importnew.com/10620.html

  • 相关阅读:
    tp5.1 多级控制器
    JS中三个点(...)是什么鬼?
    vue reqwest与fetch的使用
    new Vue({ render: h => h(App), }).$mount('#app')到底什么意思
    ant design vue 表格和国际化的使用
    JAVA日报
    JAVA日报
    JAVA日报
    JAVA日报
    JAVA日报
  • 原文地址:https://www.cnblogs.com/dobestself-994395/p/4369979.html
Copyright © 2011-2022 走看看