1.7 HashMap底层实现与源码分析

一、HashMap数据结构

//hashmap中的常量与变量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认数组长度16
static final int MAXIMUM_CAPACITY = 1 << 30;//数组最大长度
static final float DEFAULT_LOAD_FACTOR = 0.75f;//负载因子0.75,当超过负载因子值时HashMap的数组会进行两倍扩容

在这里插入图片描述

二、HashMap线程安全

HashMap是线程不安全的,与它相对的是Hashtable,两者的实现差不多,但是Hashtable实现方法里面都添加了synchronized关键字来确保线程同步,因此相对而言HashMap性能会高一些。

HashMap与Hashtable两者的差别

主要是线程一个不安全一个安全

  1. 针对null的不同
    HashMap可以使用null作为key,而Hashtable则不允许null作为key
    虽说HashMap支持null值作为key,不过建议还是尽量避免这样使用,因为一旦不小心使用了,若因此引发一些问题,排查起来很是费事。
    Note:HashMap以null作为key时,总是存储在table数组的第一个节点上
  2. 继承结构
    HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类。
  3. 初始容量与扩容
    HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。

HashMap扩容时是当前容量翻倍即:capacity2,Hashtable扩容时是容量翻倍+1即:capacity2+1
4. 两者计算hash的方法不同
Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模

int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;

HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取摸

int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
 
static int hash(int 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);
    }
 
 static int indexFor(int h, int length) {
        return h & (length-1);

三、HashMap底层实现

1.put方法

key值可以为null,放在数组第一个节点;通过hash以后将对应的key放入对应数组下,通过遍历对应下标数组的链表,相同key的替换value值返回旧value值。没有相同的直接新增节点进行头插。

 public V put(K key, V value) {
 		if (table == EMPTY_TABLE) { //是否初始化
            inflateTable(threshold);//数组传入值为10会化成最近的2的幂次方数16
        if (key == null)
            return putForNullKey(value); //null总是放在数组的第一个链表中
        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;
            //如果key在链表中已存在,则替换为新value,返回旧value
            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;
    }
 //头插将新节点插入
void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //参数e, 是Entry.next
    //如果size超过threshold,则扩充table大小。再散列
    if (size++ >= threshold)
            resize(2 * table.length);
}

2.获取方法get

首先计算hash值,然后调用indexFor()方法得到该key在table中的存储位置,得到该位置的单链表,遍历列表找到key和指定key内容相等的Entry,返回entry.value值。

public V get(Object key) {
     if (key == null)
         //返回table[0] 的value值
         return getForNullKey();
     Entry<K,V> entry = getEntry(key);
 
     return null == entry ? null : entry.getValue();
}
final Entry<K,V> getEntry(Object key) {
     if (size == 0) {
         return null;
     }
 
     int hash = (key == null) ? 0 : hash(key);
     for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
         Object k;
         if (e.hash == hash &&
             ((k = e.key) == key || (key != null && key.equals(k))))
            return e;
      }
     return null;
}

四、HashMap扩容方式

数组两倍扩容,两次遍历,将所有元素地址转移到新数组中。
在这里插入图片描述
在这里插入图片描述
链表的值反过来了,在多线程中容易出现死锁
在这里插入图片描述
下一篇:1.8 HashMap底层实现与源码分析.
下一篇:1.7 concurrentHashMap底层实现与源码分析.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值