List

Arraylist和LinkedList的区别,哪个集合是线程安全的?

ArrayList和LinkedList都是Java中常见的集合类,它们都实现了List接口。

  • 底层数据结构不同:ArrayList使用数组实现,通过索引进行快速访问元素。LinkedList使用链表实现,通过节点之间的指针进行元素的访问和操作。
  • 插入和删除操作的效率不同:ArrayList在尾部的插入和删除操作效率较高,但在中间或开头的插入和删除操作效率较低,需要移动元素。LinkedList在任意位置的插入和删除操作效率都比较高,因为只需要调整节点之间的指针,但是LinkedList是不支持随机访问的,所以除了头结点外插入和删除的时间复杂度都是0(n),效率也不是很高所以LinkedList基本没人用。
  • 空间占用:ArrayList在创建时需要分配一段连续的内存空间,因此会占用较大的空间。LinkedList每个节点只需要存储元素和指针,因此相对较小。
  • 使用场景:ArrayList适用于频繁随机访问和尾部的插入删除操作,而LinkedList适用于频繁的中间插入删除操作和不需要随机访问的场景。
  • 线程安全:这两个集合都不是线程安全的,Vector是线程安全的

ArrayList的扩容机制说一下

ArrayList在添加元素时,如果当前元素个数已经达到了内部数组的容量上限,就会触发扩容操作。ArrayList的扩容操作主要包括以下几个步骤:

  • 计算新的容量:一般情况下,新的容量会扩大为原容量的1.5倍(在JDK 10之后,扩容策略做了调整),然后检查是否超过了最大容量限制。
  • 创建新的数组:根据计算得到的新容量,创建一个新的更大的数组。
  • 将元素复制:将原来数组中的元素逐个复制到新数组中。
  • 更新引用:将ArrayList内部指向原数组的引用指向新数组。
  • 完成扩容:扩容完成后,可以继续添加新元素。 ArrayList的扩容操作涉及到数组的复制和内存的重新分配,所以在频繁添加大量元素时,扩容操作可能会影响性能。为了减少扩容带来的性能损耗,可以在初始化ArrayList时预分配足够大的容量,避免频繁触发扩容操作。
    之所以扩容是 1.5 倍,是因为 1.5 可以充分利用移位操作,减少浮点数或者运算时间和运算次数。

List 与 Set 的选用准则

  • List 保留元素插入顺序,允许重复,适合有序访问与索引查询场景

  • Set 不保证插入顺序(HashSet),或按自然/自定义顺序(TreeSet),不允许重复,适合去重与快速判重场景