List
接口的可调节大小的数组实现。它实现了三个接口,相同点都是空接口,做为一个标记出现。java
类的序列化由实现这个接口的类启动。不实现此接口的类不会使用任何状态序列化和反序列化。数组
序列化:将对象的数据写入到文件安全
反序列化:将文件中的对象的数据读取出来数据结构
只有实现这个 “可克隆” 接口,而后在类中重写 Object 中的 clone()
方法,后面经过类调用 clone 方法才能克隆成功。若是不实现这个接口,则会抛出 克隆不被支持 的异常。并发
只要实现了这个接口,就能支持快速随机访问。dom
- ArrayList 实现了 RandomAccess 接口,使用 for 循环经过索引遍历。
- LinkedList 没有实现这个接口,默认使用 iterator 进行遍历。
经过测试,他们这样各自的实现是最高效的,因此实现 RandomAccess 接口也须要根据实际场景需求来进行。工具
构造方法 | 描述 |
---|---|
ArrayList() | 构造一个初始容量为 10 的空列表 |
ArrayList(int initialCapacity) | 构造一个指定初始容量的空列表 |
ArrayList(Collection<? extends E> c) | 构造一个包含指定集合元素的列表(参数就是一个集合) |
方法 | 时间复杂度 |
---|---|
add(E e) | 添加元素到末尾,平均时间复杂度为O(1) |
add(int index, E e) | 添加元素到指定位置,平均时间复杂度为O(n) |
get(int index) | 获取指定索引位置的元素,时间复杂度为O(1) |
remove(int index) | 删除指定索引位置的元素,时间复杂度为O(n) |
remove(Object o) | 删除指定元素值的元素,时间复杂度为O(n) |
public boolean add(E e) { //调用方法对内部容量进行检查 ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { //若是 elementData 数组为空 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //若是容量不足10,扩容至默认容量10(第一次扩容的参数,此时数组还为null) minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); //将上面计算出的容量传递,继续检查 } private void ensureExplicitCapacity(int minCapacity) { modCount++; //记录修改的次数++ (主要用于迭代器中) //当前最小容量 - 数组长度 > 0(判断是否溢出) if (minCapacity - elementData.length > 0) //将第一次计算出来的容量传给 “核心扩容方法” grow(minCapacity); }
private void grow(int minCapacity) { int oldCapacity = elementData.length; //记录旧的数组容量 int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量扩容1.5倍 //若是新容量小于最小容量,则将最小容量的值赋给 新容量(若是是第一次调用add方法必然小于) if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //若是newCapacity大于数组最大容量(默认是int类型最大值) if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //建立超大数组,Integer.MAX_VALUE //建立一个新数组,将新数组的地址赋值给elementData elementData = Arrays.copyOf(elementData, newCapacity); }
建立集合的时候指定足够大的容量。源码分析
ArrayList 不是线程安全的,例如当咱们 add 操做时,elementData[size++]
这个操做并非原子操做。性能
transient
关键字修饰的对象不会被序列化。由于 elementData
是 ArrayList 的数据域,因为 ArrayList 是基于动态数组实现的, elementData
的容量一般大于实际存储元素的容量,因此只需发送有实际值的数组元素便可。测试
数据结构:ArrayList底层是数组(内存里是连续的空间),LinkedList底层链表(内存空间不连续)。
访问效率:ArrayList
比LinkedList
在随机访问的时候效率要高,由于 LinkedList
是线性的数据存储方式,因此须要移动指针从前日后依次查找。
增删效率:LinkedList
要比 ArrayList
效率要高,由于 ArrayList
增删操做要影响数组内的其余数据的下标。LinkedList
只须要改变先后的指针就能够了。
ArrayList 和 Vector的底层都是基于数组实现的动态扩容。他们的区别在于:
线程安全:Vector 使用了 Synchronized 来实现线程同步,而 ArrayList 是线程不安全的。
性能:ArrayList 在性能方面优于 Vector。
扩容:Vector 扩容每次扩容 2 倍,而 ArrayList 扩容 1.5 倍。
ArrayList和LinkedList的区别和原理
Vector
,方法上添加了 Synchronized 来实现线程同步,但已经不推荐了。Collections.synchronizedList()
方法。CopyOnWriteArrayList
类建立集合。CopyOnWriteArray 容器是一个 写时复制 的容器。
往一个容器添加元素的时候,不直接往当前容器的 Object[] 添加,而是先将当前容器 Object[] 进行复制,复制出一个新的容器。而后向新的容器里添加元素,添加完元素以后,再将原容器的引用指向新的容器。
这样作的好处是,能够对 CopyOnWrite 容器进行并发的读,而不须要加锁,由于当前容器不会添加任何元素。因此 CopyOnWrite 容器是一种 “读写分离” 的思想,读和写是不一样的容器。
缺点是每次写入都要复制一个新的数组,会形成内存浪费,垃圾回收频繁等,适合读多写少的场景。