关闭

HashMap原理分析

标签: HashMap集合框架java
211人阅读 评论(0) 收藏 举报
分类:

HashMap基于哈希表的Map接口实现,其中每个元素是一个key-value对,允许使用null值和null键。内部使用单链表的方式解决hash冲突的问题,当容量不足时,它会自动增长容量。

HashMap中的部分字段:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//默认初始容量为16
static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量为2的30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认加载因子为0.75
transient Entry[] table;//入口数组,每个Entry存储一条链表的头结点
transient int size;//入口数组中已用槽的数量
transient int modCount;//被修改的次数
int threshold;//容量阀值,用于判断是否需要调整HashMap的容量(threshold = 容量 * 加载因子)
final float loadFactor;//加载因子实际大小

HashMap的几种构造函数:

    /*指定初始容量initialCapacity和加载因子loadFactor的构造函数*/
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;//最大初始容量不允许超过2的30次方
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        //初始化字段
    }
    /*指定初始容量initialCapacity的构造函数*/
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    /*默认构造函数*/
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
    }
    /*传入一个Map作为参数的构造函数*/
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }

Entry入口数组:

Entry数组里的每个元素存储一个key-value对,当前key的hash值、以及指向下一个key-value对的指针next。

static class Entry<K,V> implements Map.Entry<K,V> {  
       final K key;  
       V value;  
       final int hash;  
       Entry<K,V> next;  
   
       Entry(int h, K k, V v, Entry<K,V> n) {  
           value = v;  
           next = n;  
           key = k;  
           hash = h;  
       }  
}
HashMap中的get操作:

public V get(Object key) {    
    if (key == null) //如果key == null,进入table[0]所指示的链表处查找 
        return getForNullKey();    

    int hash = hash(key.hashCode()); //获取key的hash值   

    for (Entry<K,V> e = table[indexFor(hash, table.length)]; // 遍历指定链表找出value值
         e != null;    
         e = e.next) {    
        Object k;    

        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))    
            return e.value;    
    }  
    //找不到值返回null
    return null;
}
/*key为null的元素存储在入口数组的第一个位置(即table[0])处所指示的链表中*/   
private V getForNullKey() {    
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {    
        if (e.key == null)    
            return e.value;    
    }    
    return null;    
}  
/*将hash值与入口数组的长度进行某种形式上的取模运算,返回指定下标*/
static int indexFor(int h, int length) {
        return h & (length-1);
    }
get方法的步骤:


代码化为:

if key == null
	Entry<K, V> head = table[0];
	for Each node in list
		if found
			return value;
	end for 
	return null;

if key != null
	int hash = key.hashCode();
	int i = hash % table.length;
	Entry<K, V> head= table[i];
	for Each node in list
		if found
			return value;
	end for  
	return null;

HashMap中的put操作:

public V put(K key, V value) {    
      if (key == null)    //若key为null,则将key-value对加入到table[0]处
          return putForNullKey(value);    
       
      int hash = hash(key.hashCode()); //获取key的hash值
      int i = indexFor(hash, table.length);  //将hash值与入口数组的长度进行取模运算返回指定位置下标
      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值对应的key-value对存在,则用新value值替换掉旧value值  
              V oldValue = e.value;    
              e.value = value;    
              e.recordAccess(this);    
              return oldValue;    
          }    
      }    
  
      // 若key值对应的key-value对不存在,则将此key-value对保存至入口数组中  
      modCount++;  
      // 将key-value对添加到table[i]处  
      addEntry(hash, key, value, i);    
      return null;    
  } 
/*putForNullKey()的作用是将key为null的key-value对添加到table[0]位置*/    
private V putForNullKey(V value) {    
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {    
        if (e.key == null) {    
            V oldValue = e.value;    
            e.value = value;    
            e.recordAccess(this);    
            return oldValue;    
        }    
    }    
    // 如果没有存在key为null的键值对,则直接添加到table[0]处 
    modCount++;    
    addEntry(0, null, value, 0);    
    return null;    
}   
/*addEntry()的作用是新增一个Entry元素。将key-value对插入指定位置,bucketIndex是位置索引*/    
void addEntry(int hash, K key, V value, int bucketIndex) {    
    // 头插法插入新Entry元素   
    Entry<K,V> e = table[bucketIndex];        
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);    
    // 若HashMap的实际大小大于或等于阀值,则重新调整HashMap的大小    
    if (size++ >= threshold)    
        resize(2 * table.length);    
}    
/*resize()的作用是重新调整HashMap的大小,newCapacity是调整后的单位*/    
void resize(int newCapacity) {    
    Entry[] oldTable = table;    
    int oldCapacity = oldTable.length;    
    if (oldCapacity == MAXIMUM_CAPACITY) {    
        threshold = Integer.MAX_VALUE;    
        return;    
    }    
  
    // 新建一个HashMap,将"旧HashMap"的全部元素添加到"新HashMap"中,    
    // 然后,将"新HashMap"赋值给"旧HashMap"。    
    Entry[] newTable = new Entry[newCapacity];    
    transfer(newTable);    
    table = newTable;    
    threshold = (int)(newCapacity * loadFactor);    
}  
/*将HashMap中的全部元素都转移到newTable中*/    
void transfer(Entry[] newTable) {    
    Entry[] src = table;    
    int newCapacity = newTable.length;    
    for (int j = 0; j < src.length; j++) {    
        Entry<K,V> e = src[j];    
        if (e != null) {    
            src[j] = null;    
            do {    
                Entry<K,V> next = e.next;    
                int i = indexFor(e.hash, newCapacity);    
                e.next = newTable[i];    
                newTable[i] = e;    
                e = next;    
            } while (e != null);    
        }    
    }    
}    
put方法的步骤:


参考自:http://blog.csdn.net/ns_code/article/details/36034955






















0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:14502次
    • 积分:544
    • 等级:
    • 排名:千里之外
    • 原创:35篇
    • 转载:10篇
    • 译文:0篇
    • 评论:1条
    文章分类