ArrayList与LinkedList

数组

以下是数组在内存中存储结构和扩容过程。

array数组长度为4,现在需要放入第五个值,需要先扩容,在后边找到能容下新数组长度的连续空间

  • 新增数据空间判断

    新增数据的时候需要判断当前是否有空闲空间存储数据

  • 扩容需要申请新的连续空闲空间

    上图为例,长度4需要扩容到8。必须找到长度为8的连续空间才能新建扩容数组

  • 把老的数组复制过去,追加新的内容

  • 回收老的数组空间

链表

  • 寻找一个元素空间插入即可,不需要连续空间
  • 链表长度大小不定
  • 需要寻址,节省空间

时间复杂度对比

  • O(1)就是最低的时空复杂度了,耗时/耗空间与输入数据大小无关,无论输入数据增大多少倍,耗时/耗空间都不变。 哈希算法就是典型的O(1)时间复杂度,无论数据规模多大,都可以在一次计算后找到目标(不考虑冲突的话)

  • O(N)就代表数据量增大几倍,耗时也增大几倍。比如常见的遍历算法,就是O(n);

总结

  • ArrayList是由数组组成,大小固定,不适合动态存储,LinkedList由双向列表组成,大小可变,扩展性强;ArrayList扩容大小为1.5倍(newCapacity = oldCapacity + (oldCapacity >> 1))
  • ArrayList查询和修改方便,只需要修改和获取指定的数组值,LinkedList新增和删除方便,因为只需要添加和删除节点
  • 数据结构都是存储在内存中,一般初始化时指定大小都是2的倍数,计算机分配空间都是使用次幂去分配,好处是能减少碎片空间,ArrayList扩容时需要查询连续的空间,而LinkedList只需要查询到下一个空间即可
  • 同样是O(N)次遍历,ArrayList快于LinkedList,因为CPU缓存一次读取多块,ArrayList获取空间块链是连续的,而Linked获取空间块链可能是跳跃的,获取下一个元素需要利用内存中的下标去查找,CPU缓存读取速度大于内存
  • forEach循环时删除元素会爆ConcurrentModificationException异常,要使用迭代器(Iterator),或者线程安全的集合,原因是java.util.ArrayList.Itr#checkForComodification方法中modCount != expectedModCount
觉得文章有用的话,欢迎点赞打赏~