HashMap

什么是HashMap
HashMap底层是一个散列表结构是数组+链表。

  1. 数组储存空间是连续的,占用内存严重,数组的特点就是容易寻址,但是插入和删除困难。
  2. 链表储存空间不是连续的,占用内存较为宽松,特点是寻址困难,插入和删除容易。

在HashMap内部存在一个静态内部类Entry[](就是它的数组) 他的属性有key,value,next


HashMap的存取实现
HashMap默认大小是16,每个key都有一个固定的hash,
存储和读取时。
int hash = key.hashCode();
int index = hash%Entry[].length;
return Entry[index];
如果存入的时候计算出来的index相同,那么触发链表,新进来的存入链表头部。
Null的key永远放在数组的第一个位置

public V get(Object key) {
 if (key == null) return getForNullKey(); 
     int hash = hash(key.hashCode()); //先定位到数组元素,再遍历该元素处的链表 
             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.equals(k))) 
                           return e.value;
              }
         return null;
}


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;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }


    private V getForNullKey() {
    
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) return e.value;
        }
        return null;
    }


hashmap的Hashcode冲突
首先确定的是HashMap的初始数组容量是16,16是2的4次方,
在HashMap中为了避免Hash冲突,也就是数组Entry[]上只有一个值,这样可以不用去再遍历链表,为了分布均匀所以采用2的幂次方,
HashCode & (Entry.length-1) (与运算& 相同为1,不同为0)


HashMap的扩容机制
HashMap得初始容量是16, loadFactor=0.75,负载因子。扩容后的数组容量是原来得2倍。

如果要存15个数,那么我们数组16长度就足够了,但是当到12的时候就会触发扩容机制,所以我们要设置长度为32。避免了HashCode冲突。


传统得HashMap得缺点

当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。

针对这种情况,JDK 1.8 中引入了 红黑树(查找时间复杂度为 O(logn))来优化这个问题。
当链表长度大于8时转化为红黑树。


HashMap 中有三个关于红黑树的关键参数:

TREEIFY_THRESHOLD
UNTREEIFY_THRESHOLD
MIN_TREEIFY_CAPACITY

值及作用如下:

//一个桶的树化阈值
//当桶中元素个数超过这个值时,需要使用红黑树节点替换链表节点
//这个值必须为 8,要不然频繁转换效率也不高
static final int TREEIFY_THRESHOLD = 8;
 
//一个树的链表还原阈值
//当扩容时,桶中元素个数小于这个值,就会把树形的桶元素 还原(切分)为链表结构
//这个值应该比上面那个小,至少为 6,避免频繁转换
static final int UNTREEIFY_THRESHOLD = 6;
 
//哈希表的最小树形化容量
//当哈希表中的容量大于这个值时,表中的桶才能进行树形化
//否则桶内元素太多时会扩容,而不是树形化
//为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD
static final int MIN_TREEIFY_CAPACITY = 64;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值