JAVA8之HashMap

类继承层次

在这里插入图片描述

底层数据结构

hash表 +(普通链表 | 红黑树)

科普搬运

hashcode

散列函数,给每个对象一个编号,计算速度越快且冲突(不同对象得到相同编号视为冲突)越小越好

计算机怎么存数

补码

为什么要用补码

让逻辑电路计算实现更简单,尤其是正和负数相加不用借位操作

原码反码补码的关系

正数:原码=反码=补码
负数:反码=原码按位取反,补码=反码+1

左移<< 右移>>

直接在补码基础上左移或者右移,左移右边补0,右移时正数左边补0,负数左边补1

如:5 << 1 等于 10
5的补码:00000000000000000000000000000101
左移1位后:00000000000000000000000000001010
如:-5 << 1 等于 -10
-5的补码:11111111111111111111111111111011
左移1位:11111111111111111111111111110110
为什么等于-10,计算机显示的时候需要对补码11111111111111111111111111110110求原码即-1再取反
即11111111111111111111111111111010

无符号右移>>>(没有<<<这种操作)

在补码的基础上左移右边补0,右移左边补0

如:7 >>> 2 等于 1
7的补码: 00000000000000000000000000000111
无符号右移2位: 000000000000000000000000000001
如:-7 >>> 2 等于 1073741822
7的补码: 11111111111111111111111111111001
无符号右移2位: 00111111111111111111111111111110
上面已经是一个正数原码就等于补码最后结果即是00111111111111111111111111111110,十进制则为2^30-2=1073741822

HashMap中的索引计算

/**
 * Computes key.hashCode() and spreads (XORs) higher bits of hash
 * to lower.  Because the table uses power-of-two masking, sets of
 * hashes that vary only in bits above the current mask will
 * always collide. (Among known examples are sets of Float keys
 * holding consecutive whole numbers in small tables.)  So we
 * apply a transform that spreads the impact of higher bits
 * downward. There is a tradeoff between speed, utility, and
 * quality of bit-spreading. Because many common sets of hashes
 * are already reasonably distributed (so don't benefit from
 * spreading), and because we use trees to handle large sets of
 * collisions in bins, we just XOR some shifted bits in the
 * cheapest possible way to reduce systematic lossage, as well as
 * to incorporate impact of the highest bits that would otherwise
 * never be used in index calculations because of table bounds.
 */
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

(n - 1) & hash n是hash table的长度
因为n为2^n次方,所以n-1则得到类似这样的二进制编码 111111
假设n=32则,n-1的二进制为:0000 0000 0000 0000 0000 0000 0001 1111
所以于hash值取&的时候就相当于只有最后5位存在非0,取值也就限定在0-31之间了。hash函数越均匀则碰撞概率越低。

默认初始容量

/**
 * The default initial capacity - MUST be a power of two.
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

什么时机触发扩容

/**
 * The next size value at which to resize (capacity * load factor).
 *
 * @serial
 */
// (The javadoc description is true upon serialization.
// Additionally, if the table array has not been allocated, this
// field holds the initial array capacity, or zero signifying
// DEFAULT_INITIAL_CAPACITY.)
int threshold;

当元素大于容量乘以装载因子时触发

扩容

/**
 * Initializes or doubles table size.  If null, allocates in
 * accord with initial capacity target held in field threshold.
 * Otherwise, because we are using power-of-two expansion, the
 * elements from each bin must either stay at same index, or move
 * with a power of two offset in the new table.
 *
 * @return the table
 */
 扩容容量为原来的两倍

什么时机普通链表膨胀为红黑树

 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
     treeifyBin(tab, hash);
     break;

    /**
     * Replaces all linked nodes in bin at index for given hash unless
     * table is too small, in which case resizes instead.
     */
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
            ....省略.....
     }

当链表元素大于等于TREEIFY_THRESHOLD(8)时且hash表容量大于MIN_TREEIFY_CAPACITY(64)

什么时机红黑树退化为普通链表

if (lc <= UNTREEIFY_THRESHOLD)
     tab[index] = loHead.untreeify(map);
 if (hc <= UNTREEIFY_THRESHOLD)
     tab[index + bit] = hiHead.untreeify(map);

当节点数少于等于UNTREEIFY_THRESHOLD(6)时退化为普通链表

主要操作时间复杂度

get(key) 为 O(1)或O(logn)
contain(key) 为 O(1)或O(logn)
contain(value) 为 O(n)或O(nlogn)
remove(key) 为 O(1)或O(logn)
remove(key, value) 为 O(1)或O(logn)

安全性

fail-fast
线程不安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值