1.ArrayList:动态数组,底层基于数组实现,访问(list.get(i))快,插入和删除比较慢
非线程安全,多个线程同时访问一个ArrayList对象,会导致数据不一致问题
扩容机制:初始容量为10,每次扩充是之前数组容量的1.5倍,扩容时会创建一个新数组,然后将旧数组中的元素复制到新数组中
2.LinkedList:双向链表,底层基于双向链表实现,访问元素慢,但是插入和删除快
LinkedList会维护链表的长度size,每增加一个元素的时候size+1,移除一个元素的时候size-1,这样查找的时候,如果查找的位置小于size的一半,从链表头结点从前往后找,如果查找的位置大于等于size的一半,从链表的尾部从后往前查找
总结:ArrayList的扩容机制就是:初始时数组大小为10(默认为10,你也可以指定为其他数)
然后每次达到容量后,新容量就扩充为原容量的1.5倍
ArrayList底层通过数组实现,为了追求效率,Arraylist没有实现同步(synchronized),即非线程安全
ArraryList扩容机制:
数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组里,每次扩容后新数组容量大约是原数组容量的1.5倍,具体扩容机制如下:
设ArrayList的容量为Capacity,如果你ArrayList array=new ArrayList(initial_capacity),那么ArrayList的初始容量就是initial_capacity,否则就为10。
添加元素时:
(1)如果 元素个数+1<=Capacity,那就放心添加元素;
(2)元素个数+1>Capacity,那ArrayList就发生扩容,容量Capacity变为1.5Capacity(其实是Capacity=Capacity+Capacity>>1,Capacity>>1是指Capacity这个数进行位运算,而且是左移运算,左移一位,所以Capacity>>1其实就是0.5个Capacity,所以Capacity+Capacity>>1就等于1.5Capacity+Capacity);
举例,初始容量为10,当向ArrayList中添加了10个元素,准备添加第十一个元素时,ArrayList先进行扩容,容量变成15,然后添加第11个元素进ArrayList。一直到添加第16个元素时,ArrayList再次进行扩容,容量变为15+15>>1=15+7.5=22.5,小数部分舍去,所以是变成22。
总结就是一句话:扩容到原容量的1.5倍
LinkedList底层是通过双向链表实现的
同时实现了List接口和Deque接口,因此它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack)
所以当你想要使用栈或者队列的时候,可以考虑使用LinkedList(Java官方已经声明不建议使用Stack类,甚至Java里面根本就没有队列Queue这个类),但是其实关于栈和队列现在首选还是ArrayDeque,当作栈和队列使用时,ArrayDeque比LinkedList性能更好
为追求效率LinkedList没有实现同步(synchronized),如果需要多个线程并发访问,可以先采用Collections.synchronizedList()
方法对其进行包装。
ArrayList和LinkedList之间的区别:
(1) ArrayList:线程不安全,LinkedList:线程不安全,即两个都是线程不安全的
(2)ArrayList采用数组存储,LinkedList采用链表存储,所以ArrayList支持高速随机元素访问,LinkedList不支持高速随机元素访问,LinkedList 在插入和删除数据时效率更高,ArrayList 查询效率更高;
最后:我们在项目中一般是不会使用到 LinkedList
的,需要用到 LinkedList
的场景几乎都可以使用 ArrayList
来代替,并且,性能通常会更好!就连 LinkedList
的作者约书亚 · 布洛克(Josh Bloch)自己都说从来不会使用 LinkedList