2、Java集合高频面试题

1、说一说集合类的体系结构

我们常见的集合主要有两大类,分别是单列集合和双列集合

  1. 单列集合的顶级接口是Collection,它下面有两个主要的子接口分别是List和Set

    List的特点是元素有序的,可以重复的;Set的特点是元素无序的,不可重复的

    List下我们常用的类有ArrayList、LinkedList等,Set下我们常用的类有HashSet、LinkedHashSet、TreeSet等

  2. 双列集合的顶级接口是Map,它的特点是每个元素都有键和值两部分组成,而且键不能重复

    Map接口下我们常用的类有:HashMap、LinkedHashMap、TreeMap等

2、聊聊集合类的底层数据结构

集合主要分为双列集合和双列集合

  1. 双列集合都是Map的实现类,主要有HashMap、LinkedHashMap和TreeMap

    • HashMap: JDK1.8之前是由数组+链表组成的,JDK1.8之后,为了提升效率,在当链表的长度>8,并且数组长度>=64的时候,链表就会转换为红黑树
    • LinkedHashMap:继承自HashMap,在HashMap的基础上增加了一条双向链表,来保持键值对的插入顺序。
    • TreeMap:底层是红黑树
  2. 单列集合主要是List和Set

    List有ArrayList和LinkedList,ArrayList底层是数组,查询快,增删慢;LinkedList底层是双向链表,查询慢,增删快

    Set有HashSet、LinkedHashSet和TreeSet,它的实现原理和对应的Map是一样的,底层都是用的对应Map的key实现

3、ArrayList和LinkedList的区别

ArrayList和LinkedList都是Java中的单列结合,都是有序的,可重复的

不同点有下面几个:

  1. 底层数据结构不同:ArrayList 底层是动态数组,而LinkedList底层是双向链表
  2. 使用场景不同:ArrayList查询快,增删慢,适合查询场景;LinkedList查询慢,增删快,适合频繁修改的场景
  3. 占用内存空间不同:LinkedList比ArrayList更占内存,这是因为它的每个节点除了存储数据,还存储了前后节点的引用两个引用

4、HashMap和HashTable区别

HashMap和HashTable都是Map的子类,都可以存储键值对的数据,区别点在于HashTable是线程安全,HashMap则不是

HashTable的线程安全是通过底层在每个方法上添加synchronized 实现的,因此它的效率要比HashMap低

HashTable在我们公司中已经不再使用,我对它的了解也不是特别多,我们公司一般都是采用的HashMap

如果碰到需要线程安全的场景,我们则会使用ConcurrentHashMap,而不用HashTable,所以我对它的了解也就这些

5、HashMap的底层原理

HashMap底层数据结构是哈希表,哈希表在JDK1.8之前是数组+链表实现,在JDK1.8之后是数组+链表+红黑树实现的

下面我以map中存储对象的流程给您说一下它的实现原理把

  1. 当我们创建一个HashMap的时候,JDK就会在内存中创建一个长度为16的数组

  2. 当我们调用put方法像HashMap中保存一个元素的时候,它会先调用key的hashCode方法计算出key的hash值

    然后使用得到hash值对数组长度取余,找出当前对象的元素在数组中的位置

  3. 接下来,它会判断算出的位置上是否有元素,如果没有,就会将此元素直接存储到当前位置上

    如果算出的位置上有元素或者是有链表,它会再调用key的equals方法跟存在元素的key做比较

    如果有一个比较得到的结果为true,则会进行值的覆盖,如果都为false,则会将元素追加在链表的末尾

当然,为了降低Hash冲突和链表长度,HashMap还做了一些优化

  1. 当元素的数量超过数组大小与加载因子的乘积的时候,就会执行扩容,扩容为原来的2倍,并将原来数组中的键重新进行hash运算,然后分配到新数组中

  2. 当链表的长度>8,并且数组长度>=64的时候,链表就会转换为红黑树,当红黑树结点数小于6时将再次转回为链表。

6、HashMap是怎么解决哈希冲突的

​ 首先,HashMap的底层有一个数组,它在保存元素的时候,会对元素的key进行hash运算,得到hash值,然后再使用hash值对数组长度取余,得到元素在数组中的位置,这样的话,不同的元素计算完毕之后,就可能会被分配到数组中的同一个位置上,这就是所谓的哈希冲突。

​ 解决hash冲突最常用的方式有链表法和开放地址法,而HashMap就是采用了链表法。具体做法就是当哈希冲突出现之后,HashMap会在发生冲突的位置上创建一个链表来保存元素,当然在JDK1.8之后,又对此做出了改进,那就是当链表的长度>8,并且数组长度>=64的时候,链表就会转换为红黑树,使得效率更高。

7、HashMap的扩容机制是怎样的

​ HashMap的扩容机制是指当HashMap中的元素个数超过数组长度乘以负载因子时,就会重新分配一个更大的数组,并将原来的元素重新计算哈希值并插入到新的数组中。

​ 在JDK1.8中,底层是调用resize方法实现扩容的,它的默认做法是:当元素个数超过数组长度的0.75倍时触发扩容,每次扩容的时候,都是扩容为原来的2倍, 扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。

8、为何HashMap的数组长度一定是2的次幂?

  1. 计算索引时效率更高:如果是 2 的 n 次幂可以使用位与运算代替取模
  2. 扩容时重新计算索引效率更高: hash & oldCap == 0 的元素留在原来位置 ,否则新位置 = 旧位置 + oldCap
  3. 为了能让 HashMap 存数据和取数据的效率高,尽可能地减少 hash 值的碰撞,也就是说尽量把数据能均匀的分配,每个链表或者红黑树长度尽量相等。我们首先可能会想到%取模的操作来实现。

9、说一下HashSet的实现原理?

​ HashSet是基于HashMap实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为present,因此 HashSet的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,由于HashMap的键是不能重复的,所有HashSet 不允许重复的值。

10、HashSet如何检查重复?

​ HashSet是一个不允许存储重复元素的集合,它通过哈希表来实现。在HashSet中,每个元素都是唯一的,如果尝试添加一个已经存在的元素,HashSet会拒绝并保留原有的元素。

​ 具体做法是在向HashSet中保存元素的时候,会先计算该元素的哈希值,然后确定这元素在数组中的位置,如果该位置上没有元素,则保存成功,如果有元素,则调用equals方法去跟存在的每个值进行比较,表较结果有一个相等,则直接丢弃;不相等,就会被挂在老元素的后面。

你可能感兴趣的:(Java面试题,java,面试)