HashMap(一)

HashMap


版本:1.8

基本特徵:

繼承的類:

AbstractMap<K,V>
實現的接口:Map <K,V> 
繼承的類:Object
直接子類:ConcurrentHashMap , ConcurrentSkipListMap , EnumMap , HashMap , IdentityHashMap , TreeMap , WeakHashMap 

實現的接口:

Serializable , Cloneable , Map <K,V> 

直接子類:

LinkedHashMap , PrinterStateReasons ,還有自己寫的繼承自hashmap的類。
  1. HashMap<K,V>是繼承自AbstractMap<K,V>的一個類。

  2. 存儲的元素是鍵值對<K,V>。

  3. 存儲的元素是無序的。

  4. 存儲的元素允許鍵值對爲空,但是值允許一個鍵爲空,多個值爲空。

  5. 不是線程安全的。

關於4:看程序說明問題

	HashMap<String,String> map01  =  new HashMap<String,String>(10);
	String key01=null;String value01=null;
	String key02=null;String value02=null;
	//存入鍵和值都爲空
	map01.put(key01,value01);
	map01.put(key02,value02);
	System.out.println(map01.size());
	//存入鍵空,值不空
	String key03=null;String value03="鍵空值不空value03";
	map01.put(key03,value03);
	System.out.println(map01.size());
	//存入鍵空,值不空
	String key04=null;String value04="鍵空值不空value04";
	map01.put(key04,value04);
	System.out.println(map01.size());
	System.out.println(map01.get(key01));
	System.out.println(map01.get(key02));
	System.out.println(map01.get(key03));
	System.out.println(map01.get(key04));
1
1
1
鍵空值不空value04
鍵空值不空value04
鍵空值不空value04
鍵空值不空value04

這是因爲,HashMap雖然允許空值,但是對於鍵,只允許一個空值存進去,之後再遇到鍵是空值的,相當於是修改空鍵所對應的value,所以上述程序存入四個鍵爲空的鍵值對,其實就是存入一個鍵,一個空值,所以HashMap的size一直是1。之後再遇到鍵是空的,就直接把之前存入的空鍵對應的值改成又遇到的值。

稍微修改一下:

HashMap<String,String> map01  =  new HashMap<String,String>(10);
	
	String key01=null;String value01=null;
	String key02=null;String value02=null;
	//存入鍵和值都爲空
	map01.put(key01,value01);
	map01.put(key02,value02);
	System.out.println(map01.size());
	//存入鍵空,值不空
	String key03="02";String value03=null;
	map01.put(key03,value03);
	System.out.println(map01.size());
	//存入鍵空,值不空
	String key04="01";String value04=null;
	map01.put(key04,value04);
	System.out.println(map01.size());
	System.out.println(map01.get(key01));
	System.out.println(map01.get(key02));
	System.out.println(map01.get(key03));
	System.out.println(map01.get(key04));
1
2
3
null
null
null
null

這是鍵不空,值空的情況。可以看出,HashMap是允許多個值爲空的,換句話說,HashMap只檢測鍵是否重複。

類的特徵:

構造方法:四個

  • HashMap()無參構造方法

  • HashMap(int initialCapacity)指定初始容量構造方法

  • HashMap(int initialCapacity, float loadFactor)指定初始容量和負載因子構造方法

  • HashMap(Map<? extends K, ? extends V> m)指定存儲元素構造方法

  1. 無參構造方法public HashMap()
public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

這裏只給負載因子賦默認值0.75f【f代表0.75是浮點型數】

  1. HashMap(int initialCapacity)指定初始容量構造方法
public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

this指的是指定初始容量和負載因子構造方法,這裏調用了HashMap(int initialCapacity, float loadFactor)並指定參數是初始容量和默認負載因子。

  1. HashMap(int initialCapacity, float loadFactor)指定初始容量和負載因子構造方法
   public HashMap(int initialCapacity, float loadFactor) {
        //判斷指定容量是不是負數,是就拋出非法參數異常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
         //判斷指定容量是不是大於哈希表的最大容量,是的話就限制指定容量縮小成哈希表的最大容量
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
         //檢測負載因子,小於等於0或者不是數字就拋出非法參數異常
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        //經上述三個if檢測修改完後,給負載因子賦值,給閾值賦值。
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

這裏介紹最後一條語句中的tableSizeFor(initialCapacity)

tableSizeFor作用:給輸入的數返回一個大於等於該數並且是2的冪次方的數。即:輸入7,那麼返回一個8.

閾值=哈希表的容量*負載因子。

   static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;//把n或n右移一位的結果賦值給n
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

哪個數來舉例:cap=9,那麼n=8

整型數據佔四個字節,最後一個字節的二進制:0000 1000

第一次右移並運算:0000 1000|0000 0100=0000 1100

第二次右移並運算:0000 1100|0000 0011=0000 1111

第三次右移並運算: 0000 1111|0000 0000=0000 1111

第四次右移並運算: 0000 1111|0000 0000=0000 1111

可知最後n=15,返回的數據是15+1=16。

在這個構造函數中,把閾值設置成大於等於初始容量的2的冪次方

  1. HashMap(Map<? extends K, ? extends V> m)指定存儲元素構造方法
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;//設置負載因子成默認值
        putMapEntries(m, false);
    }

對於:putMapEntries(m, false)函數:

 final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {//如果s=0,函數就結束了,本來就是個空的hashmap所以什麼都不用幹
            if (table == null) { // 要是本來的table是空的,
                float ft = ((float)s / loadFactor) + 1.0F;//計算閾值+1
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?//對閾值做修改
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)//如果計算的閾值比實際閾值大就重新給實際閾值賦值
                    threshold = tableSizeFor(t);//調用tableSizeFor,使實際閾值是2的冪次方
            }
            else if (s > threshold)//如果s比實際閾值大就重新調整table大小
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {//把指定map放入hashmap
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

未完,,,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章