HashMap原理分析

原创 2015年11月19日 18:50:37

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






















版权声明:本文为博主原创文章,未经博主允许不得转载。

HashMap实现原理及源码分析

在数据结构与算法中,给我们介绍了常用的几种数据结构:数组,链表,哈希表。 数组结构:其在内存分配是一段连续的内存空间,可能会占用内存空间严重,空间复杂度很大,时间复杂度小,其优点是易于寻址,...
  • fengshizty
  • fengshizty
  • 2015年04月11日 20:16
  • 1400

HashMap实现原理分析

HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。   首先...
  • vking_wang
  • vking_wang
  • 2013年11月05日 15:23
  • 291529

hashMap的原理 深入理解

首先再次强调hashcode (==)和equals的真正含义(我记得以前有人会说,equals是判断对象内容,hashcode是判断是否相等之类): equals:是否同一个对象实例。注意,是“实...
  • yanlove_jing
  • yanlove_jing
  • 2016年06月11日 19:44
  • 3232

HashMap原理解析

HashMap 实现原理
  • z55887
  • z55887
  • 2016年01月27日 22:57
  • 596

了解HashMap的get和put内部的工作原理,需要理解透Java HashMap的原理

了解HashMap的get和put内部的工作原理,需要理解透Java HashMap的原理,今天我们单说get和put 的工作原理。 一、Put : 让我们看下put方法的...
  • chajinglong
  • chajinglong
  • 2016年03月12日 22:01
  • 689

面试中HashMap的工作原理

HashMap的工作原理 这篇文章极好,还有一些其它的东西,说的也很透彻。 详情,请点击: http://www.importnew.com/7099.ht...
  • paincupid
  • paincupid
  • 2015年08月17日 00:33
  • 1406

hashmap与Hashtable实现原理浅析

原文地址:http://www.cnblogs.com/lzrabbit/p/3721067.html#h1 HashMap和Hashtable的区别两者最主要的区别在于Hashtable是线程安全,...
  • Double2hao
  • Double2hao
  • 2016年11月30日 18:21
  • 3541

面试中怎么回答HashMap的工作原理

HashMap的工作原理是近年来常见的Java面试题。几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间的区别,那么为何这道面试题如此...
  • AnneQiQi
  • AnneQiQi
  • 2016年06月04日 15:14
  • 2026

HashMap的工作原理

1.前言     在探讨HashMap源码之前,先说一下HashCode,为什么呢?因为HashMap有一个特性是Key是唯一值,如何确定key的唯一性呢,这就用到了hash算法。在HashMap(...
  • sinat_33536912
  • sinat_33536912
  • 2016年08月29日 09:57
  • 978

深入理解Java中的HashMap的实现原理

1. HashMap为了提高查找的效率使用了分块查找的原理,对象的hashCode返回的哈希值进行进一步处理,这样就有规律的把不同的元素放到了不同的区块或桶中。下次查找该对象的时候,还是计算其哈希值,...
  • sunqunsunqun
  • sunqunsunqun
  • 2015年06月22日 18:44
  • 2843
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:HashMap原理分析
举报原因:
原因补充:

(最多只允许输入30个字)