参考:
源码浅析ArrayList、LinkedList和Vector的区别
集合 | 初始容量(DEFAULT_CAPACITY) | 加载因子(DEFAULT_LOAD_FACTOR) | 扩容增量 |
---|---|---|---|
ArrayList | 10 | 1 :即当 元素个数 超过 [容量长度] 时,进行扩容 | 原容量的 0.5倍+1 ,如 ArrayList 的容量为10,一次扩容后是容量为16 |
HashMap | 16 ,是2^4 或 1 << 4,可以提高查询效率 | 0.75 :即当 元素个数 超过 [容量长度的0.75倍] 时,进行扩容 | 原容量的 1 倍 , 容量为16,一次扩容后是容量为32 |
一、ArrayList和LinkedList的区别
从实现看:
LinkedList
底层实现是双向链表
ArrayList
底层实现是动态数组
从扩容机制看:
-
LinkedList不存在扩容 的说法,因为是链表结构。
-
ArrayList底层是动态数组存在扩容说法,默认的数组大小是10,在检测是否需要扩容后,如果扩容,会扩容为原来的1.5倍大小。原理就是把老数组的元素存储到新数组里面
从优点看:
-
ArrayList的查找性能好,因为底层是数组,适用于查找元素。
-
LinkedList底层是双链表,对于插入或者删除元素来说,操作方便,性能高。
对于随机访问,ArrayList
优于LinkedList
, 对于插入和删除操作,LinkedList
优于 ArrayList
从缺点看:
-
ArrayList因为是一块连续的内存,存储数据元素,所以如果要删除或者插入一个元素,那么之前或者之后的元素都要移动,代价很高。
-
LinkedList是链表,在内存中可以是不连续的,通过指针连接结点,如果要查找元素,必须去遍历整个链表,这样就比较麻烦。
LinkedList
比ArrayList
更占内存,因为LinkedList
的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
从安全角度看:
-
ArrayList在单线程环境下是安全的,多线程环境下不是线程安全的,容易造成脏读的问题,如果要使ArrayList是线程安全的,那么可以选择使用Collections.synchronizedList(new ArrayList())
-
LinkedList在单线程环境下是安全的,多线程环境下不是线程安全的,容易造成脏读的问题,如果要使LinkedList是线程安全的,那么可以选择使用Collections.synchronizedList(new LinkedList())
LinkedList和ArrayList的数据存储都是有序的,而且元素是可以重复的。
两者add都是将元素追加到现有集合元素的末尾。
二、ArrayList源码:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
//初始默认容量
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
//实际有多少元素
private int size;
//最大容量:默认为 10,最大容量为 int 上限,减 8 是为了容错
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//.........构造方法.......
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
//........................
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;//在数组末尾追加一个元素,并修改size
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//利用 == 可以判断数组是否是用默认构造函数初始化的
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//如果确定要扩容,会修改modCount
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//需要扩容的话,默认扩容一半
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//默认扩容一半
if (newCapacity - minCapacity < 0)//如果还不够 ,那么就用 能容纳的最小的数量。(add后的容量)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//超过阈值
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);//拷贝,扩容,构建一个新数组,
}
ArrayList 底层基于【数组】实现容量大小动态可变。
扩容机制为首先扩容为原始容量的 1.5 倍。
如果1.5倍太小的话,则将我们所需的容量大小赋值给 newCapacity,
如果1.5倍太大或者我们需要的容量太大,那就直接拿 newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE 来扩容。
扩容之后是通过数组的拷贝来确保元素的准确性的,所以尽可能减少扩容操作。
elementData.length 为数组长度,表示最多可以存储多少个元素。
如果需要边遍历边 remove ,必须使用 iterator。且 remove 之前必须先 next,next 之后只能用一次 remove。
三、ArrayList 的增加或删除操作相对来说效率比较低
ArrayList 在小于扩容容量的情况下其实增加操作效率是非常高的,在涉及扩容的情况下添加操作效率确实低,删除操作需要移位拷贝,效率是低点。
因为 ArrayList 中增加(扩容)或者是删除元素要调用 System.arrayCopy 这种效率很低的方法进行处理,
所以如果遇到了数据量略大且需要频繁插入或删除的操作效率就比较低了,具体可查看 ArrayList 的 add 和 remove 方法实现,
但是 ArrayList 频繁访问元素的效率是非常高的,因此遇到类似场景我们应该尽可能使用 LinkedList 进行替代效率会高一些的。
四、arraylist:linkedlist:vector区别:
ArrayList
ArrayList就是【动态数组】
它允许所有元素,包括null。
ArrayList并不是线程安全的
其底层使用数组保存所有元素所以其操作基本上是对数组的操作。
在对 ArrayList 中存储元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,扩容原来的一半,以满足添加数据的需求。
LinkedList
LinkedList 是一个继承于AbstractSequentialList的【双向链表】。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,能对它进行队列操作,允许null元素。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 是非同步的,即非线程安全的,一种解决方法是在创建List时构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(…));
LinkedList是通过节点直接彼此连接来实现的。每一个节点都包含前一个节点的引用,后一个节点的引用和节点存储的值。当一个新节点插入时,只需要修改其中保持先后关系的节点的引用即可,当删除记录时也一样。
LinkedList相对于ArrayList来说,是可以快速添加,删除元素,ArrayList添加删除元素的话需移动数组元素,可能还需要考虑到扩容数组长度。
Vector
Vector非常类似ArrayList,是可实现自动增长的对象【数组】。是有序的,可以重复的
Vector在所有的方法上面都加了synchronized 关键字。虽然其都使用了synchronized 关键字修饰,对于单操作而言是线程安全的,但是如果是组合操作,就需要我们另行同步处理。
Vector是fail-fast机制
Vector也需要进行扩容,Vector默认增长为原来一倍。