HashMap实现原理

来源:互联网 发布:南京高新沿江网络问政 编辑:程序博客网 时间:2024/06/15 22:17

HashMap数据结构

在对hash表有一定了解的基础上,分析java中的HashMap。

JDK1.6中的hash函数

  static int hash(int paramInt)//paramInt:键的hashCode  {    paramInt ^= paramInt >>> 20 ^ paramInt >>> 12;    return paramInt ^ paramInt >>> 7 ^ paramInt >>> 4;  }    static int indexFor(int paramInt1, int paramInt2)  {    return paramInt1 & paramInt2 - 1;  }  int i = hash(paramK.hashCode());  int j = indexFor(i, this.table.length);

JDK1.7中的hash函数

final int hash(Object paramObject)//paramObject:键  {    int i = this.hashSeed;    if ((0 != i) && ((paramObject instanceof String))) {      return Hashing.stringHash32((String)paramObject);    }    i ^= paramObject.hashCode();    i ^= i >>> 20 ^ i >>> 12;    return i ^ i >>> 7 ^ i >>> 4;  }  static int indexFor(int paramInt1, int paramInt2)  {    return paramInt1 & paramInt2 - 1;  }  int i = hash(paramK);  int j = indexFor(i, this.table.length);

hash()函数的核心:

int hashCode;hashCode^= hashCode>>> 20 ^ hashCode>>> 12;int hash =hashCode^ hashCode>>> 7 ^ hashCode>>> 4;

hash()函数举例:

  • 十进制数32768(二进制1000 0000 0000 0000),运算结果是35080(二进制1000 1001 0000 1000)
  • 十进制数61440(二进制1111 0000 0000 0000),运算结果是65263(二进制1111 1110 1110 1111)

hash()算法使得hash()函数的目的是将“1”变得均匀一点,然后与HashMap的承载量length进行逻辑与运算,即(hash&length-1)这样得到的结果是一个比length小的正数,即index。
这种算法使得最低位上原hashCode的8位都参与了^运算,所以在table.length为默认值16的情况下面,hashCode任意位的变化基本都能反应到最终hash table 定位算法中,这种情况下只有原hashCode第3位高1位变化不会反应到结果中,即:0x7FFFF7FF的i=15。达到了散列的本意:尽量均匀。


处理冲突方法

  • 开放定址法
  • 再哈希法
  • 连地址法

Java中hashmap解决冲突的办法就是链地址法。


hashMap存储结构

HashMap里面实现 了一个静态内部类Entry,其重要的属性有三个:key、value、next。

static class Entry<K, V> implements Map.Entry<K, V>{    final K key;    V value;    Entry<K, V> next;    int hash;}

由此看出,HashMap的基础是一个Entry[]。

HashMap的put过程

  public V put(K paramK, V paramV)  {    if (this.table == EMPTY_TABLE) {      inflateTable(this.threshold);    }    if (paramK == null)//null总是放在数组的第一个位置      return putForNullKey(paramV);    int i = hash(paramK);    int j = indexFor(i, this.table.length);    for (Entry localEntry = this.table[j]; localEntry != null; localEntry = localEntry.next)    {//遍历链表      Object localObject1;      if ((localEntry.hash == i) && (((localObject1 = localEntry.key) == paramK) || (paramK.equals(localObject1)))) {//如果key在链表中已存在,则替换为新value        Object localObject2 = localEntry.value;        localEntry.value = paramV;        localEntry.recordAccess(this);        return localObject2;      }    }    this.modCount += 1;    addEntry(i, paramK, paramV, j);    return null;  }  void addEntry(int paramInt1, K paramK, V paramV, int paramInt2)  {    if ((this.size >= this.threshold) && (null != this.table[paramInt2])) {    //如果size超过threshold,则扩充table大小。再散列      resize(2 * this.table.length);      paramInt1 = null != paramK ? hash(paramK) : 0;      paramInt2 = indexFor(paramInt1, this.table.length);    }    createEntry(paramInt1, paramK, paramV, paramInt2);  }
  • 初始化HashMap为空
  • 第一个键值对A进来,通过计算其key得到index=0,记做:Entry[0]=A
  • 第二个键值对B进来,通过计算其key得到index=0,Entry[0]里已经有数据,执行B.next=A,Entry[0]=B
  • 第三个键值对C进来,通过计算其key得到index=0,Entry[0]中是B,执行C.next=B,Entry[0]=C

即:数组中永远存放的是最后插入的元素。

再散列rehash过程

HashMap的大小

0 0
原创粉丝点击