HashMap的實現原理--鏈表散列


hashmap的擴容因子是0.75 原因 參考:HashMap默認加載因子為什么選擇0.75?(阿里)

1.    HashMap概述

   HashMap是基於哈希表的Map接口的非同步實現。此實現提供所有可選的映射操作,並允許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。

2.    HashMap的數據結構

   在java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。這樣的結構結合了鏈表在增刪方面的高效和數組在尋址上的優勢

hashmap結構:哈希表是由數組+鏈表組成的,數組的默認長度為16 ,注意 hashtable的數組的默認長度是11(可以自動變長。在構造HashMap的時候也可以指定一個長度),數組里每個元素存儲的是一個鏈表的頭結點。而組成鏈表的結點其實就是hashmap內部定義的一個類:Entity。Entity包含三個元素:key,value和指向下一個Entity的next

3.  HashMap的存取

這些元素是按照什么樣的規則存儲到數組中呢。一般情況是通過hash(key)%(len-1)獲得,也就是元素的key的哈希值對數組長度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存儲在數組下標為12的位置。

HashMap的存儲--put:

int hash = key.hashCode(); // 這個hashCode方法這里不詳述,只要理解每個key的hash是一個固定的int值
int index = hash %( Entry[].length-1);
table[index] = value;//假定存儲鏈表頭結點的數組名為table

用table[index]表示通過hash值計算出來的、元素需要存儲在數組中的位置。先判斷該位置上有沒有存有Entity,沒有的話就創建一個Entity<k,v>對象,在該位置上插入,插入結束;如果有的話,通過鏈表的遍歷方式去逐個遍歷,通過equals方法將key和已有的key進行比較,看看有沒有已經存在的key,有的話用新的value替換老的value;如果沒有,則在table[index]插入該Entity,把原來在table[index]位置上的Entity賦值給新的 Entity的next,也即,新的Entity插入(put)的位置永遠是在鏈表的最前面(百度面試),這樣插入結束。 

打個比方, 第一個鍵值對A進來,通過計算其key的hash得到的index=0,記做:table[0] = A。一會后又進來一個鍵值對B,通過計算其index也等於0,現在怎么辦?HashMap會這樣做:B.next = A,table[0] = B,如果又進來C,index也等於0,那么C.next = B,table[0] = C;這樣我們發現index=0的地方其實存取了A,B,C三個鍵值對,他們通過next這個屬性鏈接在一起。

注:null key總是存放在Entry[]數組的第一個元素。

擴展:

按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個對象不同,則它們的hashCode不一定不同。

如果兩個不同對象的hashcode相同,就稱為沖突。沖突會導致操作哈希表的時間開銷增大,所以盡量定義好的hashCode()方法,能加快哈希表的操作。覆蓋了equals方法之后一定要覆蓋hashCode方法,原因很簡單,比如,String a = new String(“abc”);String b = new String(“abc”);如果不覆蓋hashCode的話,那么a和b的hashCode就會不同,把這兩個類當做key存到HashMap中的話就 會出現問題,就會和key的唯一性相矛盾

 HashMap的讀取--get:

先定位到數組元素,再遍歷該元素處的鏈表

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;//顯然,在尋找目標元素的時候,除了對比通過key計算出來的hash值,還會用雙等或equals方法對key本身來進行比較,兩者都為true時才會返回這個元素
        }
        return null;
}

 

遍歷規則:如下:

 

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

public class abc_test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub 
        
        Map<String,String> map=new HashMap<String,String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");
        map.put("4", "value4");
        

        //第一種:普通使用,二次取值
        System.out.println("\n通過Map.keySet遍歷key和value:");  
        for(String key:map.keySet())
        {
         System.out.println("Key: "+key+" Value: "+map.get(key));
        }
        
        //第二種:推薦,尤其是容量大時  
        System.out.println("\n通過Map.entrySet遍歷key和value");  
        for(Map.Entry<String, String> entry: map.entrySet())
        {
         System.out.println("Key: "+ entry.getKey()+ " Value: "+entry.getValue());
        }
        
        //第三種  
        System.out.println("\n通過Map.values()遍歷所有的value,但不能遍歷key");  
        for(String v:map.values())
        {
         System.out.println("The value is "+v);
        }
        
        
        //第四種
        System.out.println("\n通過Iterator 遍歷Map.entrySet的key和value: ");  
        Iterator map1it=map.entrySet().iterator();
        while(map1it.hasNext())
        {
         Map.Entry<String, String> entry=(Entry<String, String>) map1it.next();
         System.out.println("Key: "+entry.getKey()+" Value: "+entry.getValue());
        }
        
        //第五種
        System.out.println("\n通過iterator 遍歷Map.keySet的key和value: ");  
        Iterator keyIter=map.keySet().iterator();
        while(keyIter.hasNext())
        {
            String key=(String)keyIter.next();
             System.out.println("Key: "+key+" Value: "+map.get(key));
        }
        
        

    }
    
       private static void print(Collection<Person> set)
        {
            Iterator<Person> it = set.iterator();
            while (it.hasNext())
            {
                Person p = it.next();
                System.out.println(p.toString());
            }
        }

}

class test {
    
    public int d;
    public     String name;
    public int getD() {
        return d;
    }
    public void setD(int d) {
        this.d = d;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        return super.equals(obj);
    }
    
    
    
    
}

 class Person
{
      public Person(String name, int age)
        {
            this.name = name;
            this.age = age;
        }

        private String name;
        private int age;

        public String getName()
        {
            return name;
        }

        public void setName(String name)
        {
            this.name = name;
        }

        public int getAge()
        {
            return age;
        }

        public void setAge(int age)
        {
            this.age = age;
        }

        public String toString()
        {
            return "{" + name + ", " + age + "}";
        }

}

class joinDemo implements Runnable{
    
    
    

    @Override
    public void run() {
        // TODO Auto-generated method stub
        
        for(int i=0;i<10;i++){
            
            System.out.println("線程1第"+i+"次執行");
        }
        
    }
    
      
     
}

  結果為: 

通過Map.keySet遍歷key和value:
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4

通過Map.entrySet遍歷key和value
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4

通過Map.values()遍歷所有的value,但不能遍歷key
The value is value1
The value is value2
The value is value3
The value is value4

通過Iterator 遍歷Map.entrySet的key和value: 
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4

通過iterator 遍歷Map.keySet的key和value: 
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4

 

參考:遍歷HashMap的四種方法

參考:HashMap的實現原理--鏈表散列


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM