HashMap

date: 2015-09-06 19:45:38


Java最基本的数据结构有数组和链表。
数组的特点是空间连续(大小固定)、寻址迅速,但是插入和删除时需要移动元素,所以查询快,增加删除慢。
链表恰好相反,可动态增加或减少空间以适应新增和删除元素,但查找时只能顺着一个个节点查找,所以增加删除快,查找慢。
那么问题来了,有没有一种综合数组和链表优点的数据结构呢?

The answer is HashMap!

HashMap

首先看看HashMap是怎么个存储方式,下图无明确作者,在此不添加Reference了! 尊重原创!

如图,hash表就是由数据+链表组成的。首先数组每个元素存储一个链表节点,通过一定算法,比如hash(key)%length
来存储后续的节点,比如31%16=15,就放到元素15开头的链表中。


看了HashMap的源码,其实HashMap是基于一个线性的数组结构来实现的:
o n e: HashMap内部定义了一个Entry<k,v>数组
t w o: static class Entry<k,v> implements Map<k,v>,并且具有key/value/next等属性,类似JavaBean
three: 也就是,Entry[]数据存储了Map<k,v>这种结构的数据


分析一段源码,来自HashMap中的put()方法:

  public V put(K key, V value) {
          if (key == null)
              return putForNullKey(value);
          int hash = hash(key.hashCode());
          int i = indexFor(hash, table.length);
          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))) {
                  V oldValue = e.value;
                  e.value = value;
                  e.recordAccess(this);
                  return oldValue;
             }
          } 
        modCount++; 
        addEntry(hash, key, value, i);
        return null;
    }	

其实思路并不复杂,通过一个hash算法得到hash值,通过hash值得到Entry数组下标index,然后找到数组位置,通过链式结构匹配Key值,get it!
那么,问题在这,这是源码里的hash算法:
static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
我真是惊呆了,费脑筋~
虽然能猜出来其作用,大体大概是防止hash冲突,但是具体为什么这样,而不是那样,我解释不清楚了=。=!
计算index的方法,倒是很清新,用到了效率最高的位运算,其中length-1防止数组越界:
static int indexFor(int h, int length) { return h & (length-1); }


另,解决hash冲突的方法:

  1. 开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
  2. 再哈希法
  3. 链地址法
  4. 建立一个公共溢出区

显然HashMap采用的是链地址法


EOF

posted @ 2016-07-06 23:25  toto怎么会喝醉  阅读(154)  评论(0编辑  收藏  举报