HashMap的几个常考的[ 面试问题 ]和[ 回答思路,底层分析 ]

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 HashMap的几个常考的[ 面试问题 ]和[ 回答思路,底层分析 ],希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

1.哈希桶在扩容的时候要注意什么?

思路:遍历每个key值下的每个链表,要注意对链表的每个结点都要重新哈希,再进行头插;


2.两个对象的hashcode一样,那么equals一定一样吗?

3.两个对象的equals一样,那么hashcode一定一样吗?

思路(2,3问题统一思路):问题2是不一定,问题3是一定,打个比方,假设你要在字典上查找“电脑”这个词(哈希桶),首先肯定要先找到“电”这个字(哈希桶的每个元素——hashcode),找到“电”这个字,下面肯定有很多词汇例如:电视,电极,电影,电脑……等等(哈希桶每个元素下的链表),而你需要要找到对应的“电脑”这个词汇(比较对应的结点——equals)


4.HashMap<String, String> map = new HashMap<>();底层的数组多大?

思路:数组大小为零,如下图,构造方法中并没有初始化数组大小,所以默认是0;

HashMap的几个常考的[ 面试问题 ]和[ 回答思路,底层分析 ]

 第一次put时将容量变成了16,原因如下图:

HashMap的几个常考的[ 面试问题 ]和[ 回答思路,底层分析 ]

 如果第一次put,oldTab == null为真,使oldCap > 0条件为假,同时oldThr等于0不满足>0的条件,走了else,可以看到newCap被赋值为:DEFAULT_INITIAL_CAPACITY;大小是多少呢?如下图:

HashMap的几个常考的[ 面试问题 ]和[ 回答思路,底层分析 ]

 1 << 4也就是2的4次方为16,所以回答上面问题:第一次put的容量为16。


5.HashMap<String, String> map = new HashMap<>(25);底层的数组多大?

思路:如果源码不想一句一句给面试官分析,可以直接说一下下图中绿字的注释,意思是:返回给定目标容量的 2 个大小的幂;也就是说给定的容量是cap,你会返回一个这个容量大小的一个2个大小的幂,注意求出的这个数的需要对cap向上取整,(如果cap大小是10,2的3次方为8,2的4次方为16,按理来说8更接近10,但是需要向上取整,所以返回16);

        综上,此题底层数组大小为32。

HashMap的几个常考的[ 面试问题 ]和[ 回答思路,底层分析 ]


6.讲一下你知道或者了解的HashMap的源码?

思路:可以给面试官分析一下 put 的源码,如下图:

HashMap的几个常考的[ 面试问题 ]和[ 回答思路,底层分析 ]

 记得对putVal展开分析,通过ctrl加鼠标点击进去(IDEA)

//put源码分析:

//这里通过putVal调用方法
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //判断table是否初始化,若未初始化,则进行初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //计算存储索引的位置,若没有元素,直接赋值
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //若结点存在,则执行赋值操作
            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);
            else {
                //是链表
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //如果链表长度大于等于(8 - 1),则将链表转化为红黑树存储
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //如果key值存在,直接覆盖
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        //记录修改次数
        ++modCount;
        //判断是否需要扩容
        if (++size > threshold)
            resize();
        //空操作
        afterNodeInsertion(evict);
        return null;
    }

HashMap的几个常考的[ 面试问题 ]和[ 回答思路,底层分析 ]

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/130457.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!