java基础面试题——ArrayList底层和LinkedList的区别呢

java基础面试题001—ArrayList底层机制和LinkedList比较

    • 总结List、Set实际应用
  • 一、ArrayList对比LinkedList
    • 1、ArrayList的介绍:
    • 2、LinkedList的介绍:
    • 3、自我总结
  • 二、ArrayList的扩容机制
  • 三、fail-fast 与 fail-safe
  • 四、Set
    • 1、Set系列集合特点
    • 2、set集合实现类的特点

java基础面试题——ArrayList底层和LinkedList的区别呢_第1张图片

总结List、Set实际应用

  1. 面试官说:如果希望元素可以重复、有索引、索引查询要快 用哪个呢?
    ArrayList,基于数组,所以索引查询快
  2. 面试官说:如果希望元素可以重复、有索引、首尾增添速度要快 用哪个呢?
    LinkedList,基于链表,所以首尾增添速度快
  3. 面试官说:如果希望元素增删改查速度快、元素不重复、无序、无索引 用哪个呢?
    Hashset,基于哈希表
  4. 面试官说:如果希望元素增删改查速度快、元素不重复、有序、无索引 用哪个呢?
    LinkedHashSet,基于双向链表和哈希表
  5. 面试官说:如果对对象排序 用哪个呢?
    TreeSet,基于红黑树

一、ArrayList对比LinkedList

通常在面试时,不会单独问LinkedList而是把ArrayList一起来问,比如说两者进行对比。

那么,你认为这样的说法是正确的吗?

  • ArrayList 增删慢、查询快
  • LinkedList 增删快、查询慢

其实这是错误的理解。我们一起来看看真正ArrayList和LinkedList吧

1、ArrayList的介绍:

  1. 基于数组,需要连续内存。
  2. 随机访问快(指根据下标访问)

由于内存地址是连续的。如果已经知道了第一个内存地址,那么就可以通过计算得到对应的元素。 首地址+(索引*每个类型大小)

  1. 在尾部进行插入和删除,相比LinkedList并不差。但是其他部分插入和删除都会进行移动数据,因此性能低
  2. 可以利用CPU缓存,局部性原理【优点】

当没有CPU缓存时。CPU从内存读写一次大概需要几百纳秒。而CPU执行一次计算是少于纳秒级别的。

2、LinkedList的介绍:

  1. 基于双向链表,无需连续内存
  2. 随机访问慢,因为要沿着链表进行遍历。
  3. 头部和尾部进行插入和删除性能高
  4. 占用内存多【缺点】

ArrayList本身大小(不包含元素),数组长度(1000各元素,扩容后是1234大小),element 元素的大小
linkedList本身占用内存非常大。因为其中包括元素、上下指针。
在这里插入图片描述

3、自我总结

第一点:是对于数据结构来对比:

ArrayList底层是数组,所以在内存分配上需要一块连续的内存。
linkedList底层是双向链表,所以在内存分配上不需要连续的内存

第二点:对于随机访问能力:

ArrayList的随机访问快。原码中是实现了一个叫【RandomAccess接口】。通过下标索引进行过去元素。如果没有实现该接口,就会通过迭代器的next方法。
LinkedList随机访问慢。因为要沿着链表进行遍历。

第三点:对于增删查的速度:

ArrayList的尾部增删性能还可以。其它部分进行增删就比较慢了,是因为涉及增删的后面的元素,会进行复制移动数据。也就是arrayCopy操作。所以性能会受影响。
ArrayList除了头部增删性能不如LinkedList,其他部位都不逊于Linkedlist
LinedList的头尾增删性能高。中间的性能不高,甚至不如ArrayList 因为移动指针比较慢。指针沿着链表进行遍历,从而找到要操作的元素。
根据元素的内容去查找的话,时间复杂度都是0(n)。所以都不适合用来用查询。 而HashMap、TreeMap更适合用来做查询,底层是哈希表和红黑树,更适合做查询。

第四点:ArrayList底层是数组,可以充分配合CPU缓存,利用局部性原理,提升相邻元素被访问的机会。LinkedList并不可以。
第五点:LinkedList底层是双向链表,所以占用内存比较多。

二、ArrayList的扩容机制

1、初始容量
调用的是ArrayList()的无参构造,初始容量是0

调用的是ArrayList(int initialCapacity)的有参构造,会使用指定容量的数组。

调用的是addAll(Collection c) 带有集合的有参构造,会使用集合的大小来作为数组的初始容量。

2、扩容机制

  • add(Object o)首次扩容为10,再次扩容为上次扩容的1.5倍。【1.5倍:扩容的数 + 右移一位】例如15+7=22
    依次:0 10 15 22 33 49 74 109 163 244,366,549,823,123,2776

  • addAll(Collection c) 没有元素时,扩容为Math.max(10,实际元素个数),有元素时为Math.max(原容量1.5倍,实际元素个数)。 下次扩容的容量跟实际元素的个数,取最大值最为真正扩容的容量。

比如从0插入11个元素,那么容量就是11。 比如插入11插入2两个元素,那么容量是从10的基础1.5倍,就是15,实际只有13个元素。

来无参构造来说:
初始容量为0,第一次扩容是10. 第二次扩容是,上一次容量的1.5倍。
扩容是创建一个新数组,把就数组的元素拷贝到新数组中,再把新元素追加到新数组中的最后一个位置。
旧数组没有人引用,会作为垃圾,然后垃圾回收掉。

三、fail-fast 与 fail-safe

ArrayList是fail-fast的典型代表,遍历的同时不能修改,尽快失败

CopyOnWriteArrayList是fail-safe的典型代表,遍历的同时可以修改,原理是读写分离。

四、Set

1、Set系列集合特点

  • 无序:存取顺序不一致。指的是只无序一次,后面的都是相同的顺序
  • 不重复:可以去除重复
  • 无索引:没有带索引的方法,所以不能使用普通的for循环遍历,也不能通过索引获取元素

2、set集合实现类的特点

  • HashSet:无序、不重复、无索引
  • LinkedHashSet:有序、不重复、无索引
  • TreeSet:排序、不重复、无索引。

你可能感兴趣的:(《程序员必学的基础》,java,链表,面试)