HashMap详解

  HashMap是java中的一种数据类型,它实现了Hash表这种数据结构。这是我们首先要知道的。

首先要了解的:  

1:HashMap是基于数组来实现Hash表的,数组就像是内存空间,它的每一个index就是一个内存的地址,即数组的下标就好比代表了一个内存地址。

2:HashMap的每一个记录就是一个Entry<K,V对象,数组中存储的就是这些对象。

3:HashMap的哈希函数就是=计算出hashcode+计算出数组下标index。

4:HasmMap使用链地址法来解决冲突,每一个Entry<>对象都有一个引用next来指向下一个Entry<>。

HashMap相比HashTable来说,它允许使用NULL值,并且非同步,除此之外,大体相同。

来看一下常见的HashMap:

下面来看一下HashMap的存储(put)源码,方便我们更好的理解它的实现方式。


public V put(K key, V value){
  if (table == EMPTY_TABLE) {
        inflateTable(threshold);//table会被初始化为长度16,且hashSeed会被赋值;
    }
  if(key == null){
        return putForNullKey(value); //如果为null,就调用putForNullKey方法处理
   }
  // a). 计算key的hashCode,下面详细说
    int hash = hash(key);
  // b). 根据hashCode计算index
    int i = indexFor(hash, table.length);
  // c). 做覆盖,遍历index位置的Entry链表,*不是解决*冲突
  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)))
    // hashCode和equals都相等则表明:本次put是覆盖操作,下面return了被覆盖的老value
     V oldvalue = e.value;
     e.value = value;
     e.recordAccess(this);
     return oldValue;
}
   //如果i索引处的Entry为null,则表明该处还没有Entry
   modCount++;
    // d). 添加Entry,并解决冲突
    // 如果需要增加table长度(size>threshold)就乘2增加,并重新计算每个元素在新table中的位置和转移
    addEntry(hash, key, value, i);
    return null;//增加成功最后返回null
}
}

/** a). 为了防止低质量的hash函数,HashMap在这里会重新计算一遍key的hashCode **/
final int hash(Object k) {
    int h = hashSeed;
    if (0 != h && k instanceof String) {//字符串会被特殊处理,返回32bit的整数(就是int)
        return sun.misc.Hashing.stringHash32((String) k);
    }

    h ^= k.hashCode();//将key的hashCode与h按位异或,最后赋值给h

    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

 

/**
 * b). 计算此hashCode该被放入table的哪个index
 */
static int indexFor(int h, int length) {
    return h & (length-1);//与table的length - 1按位与,就能保证返回结果在0-length-1内
}

通过按位与保证索引值总是在位于Table的数组内

/**
 * 解决冲突:链地址法
 * d).  addEntry(hash, key, value, i)最终是调用了此函数
 */
void createEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];// table是一个普通数组,每一个数组都有固定的长度,它的长度就是HashMap的容量
    // put添加新元素是直接new Entry放在链头,如果有老的(有冲突)则将next设置为老的,如果没有正好设置next为null
    table[bucketIndex] = new Entry<>(hash, key, value, e);// 在构造函数中,e代表next
    size++;
}

综上所述:当HashMap添加key-value时,先由key的hashCode返回值决定该key-value的存储位置,即上诉图片的最前面那一列(0,1,2,3,4......),当两个Entry的hashCode返回值相同时,将由key通过equals比较来判断是采取覆盖行为(返回true)还是采取产生Entry链行为(返回false)。

 

所以,如果要保证使用HashMap高效率搞性能,应该从三方面保证:

1:提供高效的hash算法,保证hash值尽量不同

2:提供高效的算法,保证Hash值到内存地址(数组索引)的映射速度。

3:根据内存地址(数组下标)可以迅速找到对应的值。(尽量不要产生Entry链)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值