HashMap面试必知

HashMap是java开发中常用的一个,也是面试中几乎必问的一个知识点,英文HashMap中涉及的知识点很多(考察面试者的java础、数据结构、及源码阅读),下面就HashMap的一些知识进行详谈。

首先我们看源码中定义的一些变量:

以上变量也是HashMap中非常重知识点。下面我们就一一进行详解:

DEFAULT_INITIAL_CAPACITY:HashMap的初始化容量16

MAXIMUM_CAPACITY:HashMap最大容量2^30=1073741824

DEFAULT_LOAD_FACTOR:负载因子初始值0.75f,假如容量为16,那么当容量使用至12的时候就会开始扩容。

TREEIFY_THRESHOLD :链表长度大于8的时候,转换成红黑树

UNTREEIFY_THRESHOLD:红黑树节点小于6转换为链表

MIN_TREEIFY_CAPACITY:数组大小大于64时转换为红黑树

HashMap插入过程

      

  final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
		//1、判断数组是否为空,为空初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
		//2、不为空,计算key的hash值,通过(n-1)&hash计算应该存放在数组中的下标index,
		//判断table[index]是否存在数据,不存在构造node节点并将其存放在table[index]中
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
		//存在数据时
        else {
            Node<K,V> e; K k;
			//判断key和hash值是否相等,相等的则将p值赋值给e
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
		    //不相等判断是否属于树节点,属于树节点,则插入到红黑树中;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            //如果不是树节点,创建Node加入链表末尾,判断链表的长度是否大于8,大于的话链表转换为红黑树。
			else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
			//如果e不为空,说明存在重复的key值,用新值替换旧值
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
				//onlyIfAbsent初始值为false
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
		//判断当前节点数,是否达到扩容的阀值,大于阀值则扩容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
  1. 判断数组是否为空,为空则进行初始化。
  2. 不为空,计算key的hash值,通过(n-1)&hash计算应该存放在数组中的下标index
  3. 查看table[index]是否存在数据,没有数据就构造一个node节点存放在table[index]中;
  4. 存在数据,说明发生了hash冲突(存在两个节点key的hash值一样),计算判断key是否相等,相等的话,用新的value替换原来的数据。
  5. 如果不相等,判断当前节点类型是不是树节点,如果是树节点,创造树节点插入红黑树中;
  6. 如果不是树型节点,创建普通的Node加入链表当中;判断链表的长度是否大于8,大于的话链表转换为红黑树。
  7. 插入完成后判断当前节点数是否大于阀值,如果大于阀值则开始扩容。

HashMap初始化?

一般如果new HashMap不传值,默认大小16,负载因子是0.75。提供了四种初始化的方法

HashMap()  默认大小16,负载因子是0.75

HashMap(int initialCapacity, float loadFactor)

HashMap(int initialCapacity)

HashMap(Map<? extends K, ? extends V> m)

初始化大小的函数

static final int tableSizeFor(int cap) {

          int n = cap - 1;

          n |= n >>> 1;

          System.out.println(n);

          n |= n >>> 2;

          System.out.println(n);

          n |= n >>> 4;

          System.out.println(n);

          n |= n >>> 8;

          System.out.println(n);

          n |= n >>> 16;

          System.out.println(n);

          return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;

      } 

构造函数传入初始化值initialCapacity,并不是直接设置HashMap的容量值为initialCapacity.

而是通过上面的函数计算出一个大于等于initialCapacity的2的整数次方的最小整数。

下面给大家举个例子以10为例子,大于10的最小2的整数次方数是16

原始数

二进制

initialCapacity 10

0000  1010

-1

0000  1001

n >>> 1

0000  0100

  n |= n >>> 1;

0000  1101

n >>> 2

0000  1100

  n |= n >>> 2;

0000  1101

n >>> 4

0000  0000

n |= n >>> 4;

0000  1101

n >>> 8

0000  0000

n |= n >>> 8;

0000  1101

n >>> 16

0000  0000

n |= n >>> 16;

0000  1101

return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;

16

HashMap初始化容量大小的参数为10,其实HashMap的实际初始化大小为16

Hash函数:

static final int hash(Object key) {

        int h;

        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

为什么这样设计?

  1. 尽可能的降低hash碰撞,越分散越好;
  2. 算法的高效性,采用位运算,保证高频操作效率。

本文有借鉴右侧 https://blog.csdn.net/zhengwangzw/article/details/104889549  网址博客

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值