JDK源码阅读_HashMap

JAVA源码分析_HashMap

一.HashMap概述

HashMap是基于哈希表的Map接口的非同步实现(Hashtable跟HashMap很像,唯一的区别是Hashtalbe中的方法是线程安全的,也就是同步的)。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

二.HashMap的数据结构

HashMap的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。

HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

Entry类的代码:

	/** Entry是单向链表。    
	* 它是 “HashMap链式存储法”对应的链表。    
	*它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数  
	**/  
static class Entry<K,V> implements Map.Entry<K,V> {    
    final K key;    
    V value;    
    // 指向下一个节点    
    Entry<K,V> next;    
    final int hash;    

    // 构造函数。    
    // 输入参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)"    
    Entry(int h, K k, V v, Entry<K,V> n) {    
        value = v;    
        next = n;    
        key = k;    
        hash = h;    
    }    

    public final K getKey() {    
        return key;    
    }    

    public final V getValue() {    
        return value;    
    }    

    public final V setValue(V newValue) {    
        V oldValue = value;    
        value = newValue;    
        return oldValue;    
    }    

    // 判断两个Entry是否相等    
    // 若两个Entry的“key”和“value”都相等,则返回true。    
    // 否则,返回false    
    public final boolean equals(Object o) {    
        if (!(o instanceof Map.Entry))    
            return false;    
        Map.Entry e = (Map.Entry)o;    
        Object k1 = getKey();    
        Object k2 = e.getKey();    
        if (k1 == k2 || (k1 != null && k1.equals(k2))) {    
            Object v1 = getValue();    
            Object v2 = e.getValue();    
            if (v1 == v2 || (v1 != null && v1.equals(v2)))    
                return true;    
        }    
        return false;    
    }    

    // 实现hashCode()    
    public final int hashCode() {    
        return (key==null   ? 0 : key.hashCode()) ^    
               (value==null ? 0 : value.hashCode());    
    }    

    public final String toString() {    
        return getKey() + "=" + getValue();    
    }    

    // 当向HashMap中添加元素时,绘调用recordAccess()。    
    // 这里不做任何处理    
    void recordAccess(HashMap<K,V> m) {    
    }    

    // 当从HashMap中删除元素时,绘调用recordRemoval()。    
    // 这里不做任何处理    
    void recordRemoval(HashMap<K,V> m) {    
    }    
	}

三.HashMap源码分析

  1. 关键属性

     transient Entry[] table;//存储元素的实体数组
    
     transient int size;//存放元素的个数
    
     int threshold; //临界值   当实际大小超过临界值时,会进行扩容threshold = 加载因子*容量
    
     final float loadFactor; //加载因子
     
     transient int modCount;//被修改的次数
    

解读:

loadFactor加载因子是表示Hsah表中元素的填满的程度,默认值0.75。加载因子越大,填满的元素越多,空间利用率高了,但冲突的机会加大,链表长度会越来越长,查找效率降低。

  1. 构造方法

    public HashMap(int initialCapacity, float loadFactor) {
      //确保数字合法
      if (initialCapacity < 0)
          throw new IllegalArgumentException("Illegal initial capacity: " +
                                            initialCapacity);
      if (initialCapacity > MAXIMUM_CAPACITY)
          initialCapacity = MAXIMUM_CAPACITY;
      if (loadFactor <= 0 || Float.isNaN(loadFactor))
          throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    
     // Find a power of 2 >= initialCapacity
     int capacity = 1;   //初始容量
     while (capacity < initialCapacity)   //确保容量为2的n次幂,使capacity为大于initialCapacity的最小的2的n次幂
         capacity <<= 1;
    
     this.loadFactor = loadFactor;
     threshold = (int)(capacity * loadFactor);
     table = new Entry[capacity];
    init();
    }
    
    public HashMap(int initialCapacity) {
     this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    
    public HashMap() {
     this.loadFactor = DEFAULT_LOAD_FACTOR;
     threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
     table = new Entry[DEFAULT_INITIAL_CAPACITY];
    init();
    }
    

解读:三个构造方法,默认构造器初始容量为16,加载因子0.75。

  1. 重要方法

put方法

	public V put(K key, V value) {
     // 若“key为null”,则将该键值对添加到table[0]中。
         if (key == null) 
            return putForNullKey(value);
     // 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。
         int hash = hash(key.hashCode());
     //搜索指定hash值在对应table中的索引
         int i = indexFor(hash, table.length);
     // 循环遍历Entry数组,若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
         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))) { //如果key相同则覆盖并返回旧值
                  V oldValue = e.value;
                 e.value = value;
                 e.recordAccess(this);
                 return oldValue;
              }
         }
     //修改次数+1
         modCount++;
     //将key-value添加到table[i]处
     addEntry(hash, key, value, i);
     return null;
	}

解读:

get方法

功能:

	public V get(Object key) {   
    if (key == null)//检查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;   
	}

解读:

resize()方法

功能:重新调整HashMap的大小

	void resize(int newCapacity) {
      Entry[] oldTable = table;
      int oldCapacity = oldTable.length;
      if (oldCapacity == MAXIMUM_CAPACITY) {
          threshold = Integer.MAX_VALUE;
          return;
     }

     Entry[] newTable = new Entry[newCapacity];
     transfer(newTable);//用来将原先table的元素全部移到newTable里面,并重新计算元素在数组中的索引位置
     table = newTable;  //再将newTable赋值给table
     threshold = (int)(newCapacity * loadFactor);//重新计算临界值
	}

解读:当HashMap中的元素个数超过数组大小loadFactor时,就会进行数组扩容,默认元素个数是160.75=12个。数组大小拓展为16*2=32,复制数组并重新计算元素位置。如果预知元素的个数,初始化指定HashMap数组长度将提供HashMap的性能。

posted @ 2017-10-23 11:25  Free丶Will  阅读(121)  评论(0)    收藏  举报