Young87

当前位置:首页 >个人收藏

Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day12】—— 集合框架2(HashMap)

  大家好,我是陈哈哈,北漂五年。认识我的朋友们知道,我是非科班出身,半路出家,大学也很差!这种背景来北漂,你都不知道你会经历什么🙃🙃。

  不敢苟同,相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题的形式,带你过一遍热门面试题及恰如其分的解答。当然,我不会太深入,因为我怕记不住!!

  因此,不足的地方希望各位在评论区补充疑惑、见解以及面试中遇到的奇葩问法,希望这100天能够让我们有质的飞越,一起冲进大厂!!,让我们一起学(juan)起来!!!

在这里插入图片描述
来自群里小姐姐爬山加班儿时所拍摄~~ 坐标:西安 骊山

作者:FEI



  本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识集合容器并发编程JVMSpring全家桶MyBatis等ORMapping框架MySQL数据库Redis缓存RabbitMQ消息队列Linux操作技巧等。

面试题1:说一下 HashMap 的实现原理?

正经回答:

  众所周知,HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry(包括Key-Value),其中Key 和 Value 允许为null。这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干。另外,HashMap数组每一个元素的初始值都是Null。

在这里插入图片描述

  值得注意的是:HashMap不能保证映射的顺序,插入后的数据顺序也不能保证一直不变(如扩容后rehash)。

  要说HashMap的原理,首先要先了解他的数据结构,

在这里插入图片描述

  如上图为JDK1.8版本的数据结构,其实HashMap在JDK1.7及以前是一个“链表散列”的数据结构,即数组 + 链表的结合体。JDK8优化为:数组+链表+红黑树

  我们常把数组中的每一个节点称为一个。当向桶中添加一个键值对时,首先计算键值对中key的hash值(hash(key)),以此确定插入数组中的位置(即哪个桶),但是可能存在同一hash值的元素已经被放在数组同一位置了,这种现象称为碰撞,这时按照尾插法(jdk1.7及以前为头插法)的方式添加key-value到同一hash值的元素的最后面,链表就这样形成了。

  当链表长度超过8(TREEIFY_THRESHOLD - 阈值)时,链表就自行转为红黑树。

注意:同一hash值的元素指的是key内容一样么?不是。根据hash算法的计算方式,是将key值转为一个32位的int值(近似取值),key值不同但key值相近的很可能hash值相同,如key=“a”和key=“aa”等。

  通过上述回答的内容,我们明显给了面试官往深入问的多个诱饵,根据我们的回答,下一步他多可能会追问这些问题:

1、如何实现HashMap的有序?
4、put方法原理是怎么实现的?
6、扩容机制原理 → 初始容量、加载因子 → 扩容后的rehash(元素迁移)
2、插入后的数据顺序会变的原因是什么?
3、HashMap在JDK1.7-JDK1.8都做了哪些优化?
5、链表红黑树如何互相转换?阈值多少?
7、头插法改成尾插法为了解决什么问题?

  而我们,当然是提前准备好如何回答好这些问题!当你的回答超过面试同学的认知范围时,主动权就到我们手里了。
在这里插入图片描述

深入追问:

追问1:如何实现HashMap的有序?

使用LinkedHashMap 或 TreeMap。

  LinkedHashMap内部维护了一个单链表,有头尾节点,同时LinkedHashMap节点Entry内部除了继承HashMap的Node属性,还有before 和 after用于标识前置节点和后置节点。可以实现按插入的顺序或访问顺序排序。

/**
 * The head (eldest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> head;

/**
  * The tail (youngest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> tail;
//将加入的p节点添加到链表末尾
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
  LinkedHashMap.Entry<K,V> last = tail;
  tail = p;
  if (last == null)
    head = p;
  else {
    p.before = last;
    last.after = p;
  }
}
//LinkedHashMap的节点类
static class Entry<K,V> extends HashMap.Node<K,V> {
  Entry<K,V> before, after;
  Entry(int hash, K key, V value, Node<K,V> next) {
    super(hash, key, value, next);
  }
}

示例代码:

public static void main(String[] args) {
  Map<String, String> linkedMap = new LinkedHashMap<String, String>();
  linkedMap.put("1", "占便宜");
  linkedMap.put("2", "没够儿");
  linkedMap.put("3", "吃亏");
  linkedMap.put("4", "难受");

  for(linkedMap.Entry<String,String> item: linkedMap.entrySet()){
    System.out.println(item.getKey() + ":" + item.getValue());
  }
}

输出结果:

1:占便宜
2:没够儿
3:吃亏
4:难受

追问2:那TreeMap怎么实现有序的?

  TreeMap是按照Key的自然顺序或者Comprator的顺序进行排序,内部是通过红黑树来实现。

  1. TreeMap实现了SortedMap接口,它是一个key有序的Map类。
  2. 要么key所属的类实现Comparable接口,或者自定义一个实现了Comparator接口的比较器,传给TreeMap用于key的比较。
TreeMap<String, String> map = new TreeMap<String, String>(new Comparator<String>() {

    @Override
    public int compare(String o1, String o2) {
            return o2.compareTo(o1);
    }
    
});

追问3:put方法原理是怎么实现的?

该条问答摘自 安琪拉的博客(https://blog.csdn.net/zhengwangzw

除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog

上一篇: 阿里云盘 PC 版上线,百度网盘 SVIP 功能全免费

下一篇: 球迷 如何在Linux纯命令行玩转谷歌浏览器,边看欧洲杯,边看足球宝贝

精华推荐