美文网首页
面试题-Java集合

面试题-Java集合

作者: ging_efcf | 来源:发表于2020-07-21 16:14 被阅读0次

    前言

    Java集合部分的题目,是我根据Java Guide的面试突击版本V3.0再整理出来的,其中,我选择了一些比较重要的问题,并重新做出相应回答,希望对大家起到一定的帮助。

    Java集合

    1. 说说Arraylist 与 LinkedList 区别?
      • 底层数据结构:ArrayList是数组,LinkedList是链表,那么问题就变成了数组和链表这两种数据结构的区别
      • 增删、查的平均时间复杂度:
        • 插入和删除(注意,这里的插入和删除并不包括查找,仅仅看操作本身的时间复杂度。另外,下面讨论的前提是需要维护数组有序。
          • 数组如果插(删除)在尾部,这种情况是最优时间复杂度,为O(1),如果插(删除)在头部,这种情况是最差时间复杂度,为O(N);平均时间复杂度为 (1+2+3+...n)n/2 = O(N)。
            • 注意:如果数组是无序的,完全可以把待插入数据和插入位置的数据交换一下,实现O(1)的时间复杂度。
          • 链表插入(删除)的时间复杂度,最好最差和平均都是O(1)
        • 查找
          • 数组根据下标的随机查找,最好最坏平均都是O(1)
          • 链表根据下标的随机查找,最好是O(1),最坏是O(N),平均为O(N)
            • JAVA实现中优化了链表根据下标查找的过程,先判断待查找的索引下标是在前半段还是后半段,如果在前半段,就从头开始查;如果在后半段,就从尾开始查。
          • 数组和链表,根据值查找,都需要遍历,时间复杂度都为O(N)
      • 空间复杂度:
        • 链表需要额外存储指针数据,数组不需要,但是JAVA实现中会冗余一些数组位置,也增加了空间复杂度。
    2. 说⼀说 ArrayList 的扩容机制
      • 扩容原理:ArrayList的add方法,在真正add之前,会检查是否需要扩容,扩容的关键就是确定新的数组长度,java源码中是按照原始长度的1.5倍来确定新长度的。具体的拷贝过程,就是建立一个新的长度的数组,把原始数组中的数据拷贝到新数组中
      • 最佳实践:在已知需要加入list中的元素个数时,可以调用ensureCapacity方法来提前扩容到指定的大小,避免添加的过程中不断扩容,提高性能
    3. 说一说HashMap的底层数据结构
      • JDK1.8之前,HashMap的底层数据结构是数组+链表
      • JDK1.8之后,Java优化了HashMap的链表部分,当链表达到默认阈值8时,会转变为红黑树。
    4. HashMap的扩容机制
      • 和ArrayList类似,初始都是一个空的数组,当实际添加数据时,会初始化一个默认长度为16的数组
      • 负载因子:HashMap的扩容依据 是当当前容器中的键值对个数 大于 数组长度×负载因子=负载个数时,进行扩容,扩容为之前长度的2倍。
    5. HashMap的长度为什么是2的幂次方
      • hash值是int类型的,默认有很大的范围,但是数组又不可能那么大,所以需要按照数组的实际大小取模
      • 位运算的效率高于取模的效率,所以要尽可能转化为位运算。
        • 当长度为2的幂次方时,h%length取模的操作可以转化为h&(length-1)
    6. HashMap 多线程操作导致死循环问题:这个问题的关键就是在rehash的过程中,多线程导致环形链表。HashMap本身就是线程不安全的,多线程的情况下应该使用ConcurrentHashMap。
    7. ConcurrentHashMap的并发处理
      • 我查阅了1.8的源码,在key或者value为null的时候,会抛出空指针异常。
      • 在链表头部加锁,和1.7版本的分段锁相比,锁的粒度更细,并发度更高

    相关文章

      网友评论

          本文标题:面试题-Java集合

          本文链接:https://www.haomeiwen.com/subject/uvaikktx.html