HashMap--面试详解


面试场景
面试官:

1:你对HashMap了解吗?


我回答:

  • key-value形式存放数据,支持,null-null,null-value,key-null,key-value 四种形式的。
  • 线程不安全。
  • 数据结构 数组+链表+红黑树来存放数据。
  • 实现Map接口
  • 效率较HashTable来说比较快

面试官:

2:刚说线程不安全,那如果要线程安全该如何处理?

我回答:

  • 使用Collections.synchronizedMap.(Map) 来进行线程安全处理
  • 使用ConcurrentHashMap类来代替,该类使用分段锁,比HashTable效率要快。

面试官:
点点头,还不错,看来是有准备的。

3:刚说数据结构是 数组+链表+红黑树,可以说下HashMap 是如何用该结构存数据的吗?

我回答:

  • HashMap 内部会判断是否是第一次put,如果是第一次那么会调用resize()方法进行初始化,申请数组长度为 n = 16, threshold(阈值) = 12;之后通过tab[i = (n - 1) & hash]) ==null 来判断该下标是否已经存在值,第一次put,肯定没有值,那么 通过 i=(n-1)&hash 计算出第一次put值得下标,并把值放入Node []中,可以看到下标的取值,取决于key的hash值。
  • 当put第二个值时,假设第二次的hash值和第一次的hash值相同,那么就会出现散列冲突情况。然后把第二个值添加到第一个值得next行程链表,当链表的长度>=TREEIFY_THRESHOLD(8) - 1 时转换为红黑树。

面试官:
哟呵,可以啊

4:刚才说的是通过hash值该进行下标定位,那么hash值时如何计算的,计算为什么使用^(异或) 运算符 ?

我回答:

/**
* 计算hash值
*/
 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
 /**
 * get(key) 中调用该方法
 */
 final Node<K, V> getNode(int hash, Object key) {
       ......
        if ((tab = table) != null && (n = tab.length) > 0 &&
        //注意这里的取值
                (first = tab[(n - 1) & hash]) != null) {
       ......
        return null;
    }

主要是因为
(1):在 get(key) 方法中 是通过 tab[(n - 1) & hash]) 中来定位下标的;
(2):在这如果不使用 ^ (h >>>16) 运算符, 假设 n = 16 (初始长度),A对象hash值为: 10000000,B对象的hash值为11000000;那么 就是 15(00001111) 和 hash值来取数,15分别于A,B 进行&运算 得到的结果都为 0 ;这样就会导致key值重复;如果要是加上 ^(h >>> 16) A 右移16位位 8个0,然后与 A的hashCode进行 ^ 进行运算 得到 10000000 还是原来的值,B同样也还是得到原来的值为 11000000 ;这样就不重复了;
(3):总结,使用该运算符,就是为了避免hash值不重复。因为在定位下标的时候要用;

面试官:
在下标定位时

5:为什么使用(n-1) & hash 取代了 hash % 16 ,来进行下标的定位?

/**
 *  put(key,value) 方法中调用该方法
 */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
       ......
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
           ......
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

有会有人问为什么使用&运算符代替%运算符;其实问的是同一个问题。
答案:& 预算比 % 运算 效率高 ; 因为 & 是二位运算符,直接和0,1打交道的。

面试官:
刚才在说put的过程中,有几个数值:

6:n(默认数组长度)=16,threshold(扩容阈值) = 12,treeify_threshold(转换数阈值)=8,这几个值为什么是这几个数,而不是别的数呢?

我回答:

  • n=16,因为 16=15-1 =(2的次幂数),然后15 = 1111
  • threshold = 12 = 16*0.75
  • 之后补充
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值