1ArrayList解析
1.1 首先,我们来看一下ArrayList的属性:
ArrayList底层其实就是一个数组,ArrayList中有扩容这么一个概念,正因为它扩容,所以它能够实现“动态”增长。
1.2 我们来看看构造方法来印证我们上面说得对不对:
1.3 add方法可以说是ArrayList比较重要的方法了,我们来总览一下:
1 add(E e)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 如果是第一次添加元素:默认的内部数组为DEFAULTCAPACITY_EMPTY_ELEMENTDATA, 我们就初始化容量为:DEFAULT_CAPACITY = 10。
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //想要得到最小的容量(不浪费资源)
}
ensureExplicitCapacity(minCapacity);
}
随后调用ensureExplicitCapacity()来确定明确的容量,我们也来看看这个方法是怎么实现的:
所以,接下来看看grow()是怎么实现的:
add(E e)的基本实现:
1 检查数组容量是是否足够(通过 size + 1 与 内部数组的大小 作比较):
足够的话,直接添加;
不够的话,扩容为原来的1.5倍;
2 add(int index, E element)
检查下标是否越界
空间检查,如果有需要进行扩容
插入元素
我们发现,与扩容相关ArrayList的add方法底层其实都是arraycopy()
来实现的,看到arraycopy()
,我们可以发现:该方法是由C/C++来编写的,并不是由Java实现:
3 get()方法
4set()方法
5remove()方法
检查下标
删除元素
计算出需要移动的个数,并移动
设置为null,让Gc回收
总结:
1 ArrayList是基于动态数组实现的,在增删时候,需要数组的拷贝复制。
2 ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍。
3 删除元素时不会减少容量,若希望减少容量则调用trimToSize()。
4 它不是线程安全的。
5 它能存放null值。
2 Vector
Vector是jdk1.2的类了,比较老旧的一个集合类:
Vector底层也是数组,与ArrayList最大的区别就是:同步(线程安全) synchronized
修饰的同步方法:
如果想要ArrayList实现同步,可以使用Collections的方法:List list = Collections.synchronizedList(new ArrayList(...));,
就可以实现同步了。
ArrayList在底层数组不够用时在原来的基础上扩展0.5倍,Vector是扩展1倍:
3LinkedList
LinkedList底层是双向链表,LinkedList实现了Deque接口,因此,我们可以操作LinkedList像操作队列和栈一样。
1构造方法
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
2add方法
add方法实际上就是往链表最后添加元素:
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
3remove方法
4get方法
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
5set方法
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
4总结
ArrayList:
1 底层实现是数组
2 ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍
3 在增删时候,需要数组的拷贝复制(navite 方法由C/C++实现)
LinkedList:
底层实现是双向链表[双向链表方便实现往前遍历]
Vector:
底层是数组,现在已少用,被ArrayList替代,原因有两个:
Vector所有方法都是同步,有性能损失。
Vector初始length是10 超过length时 以100%比率增长,相比于ArrayList更多消耗内存。
总的来说:查询多用ArrayList,增删多用LinkedList。
ArrayList增删慢不是绝对的(在数量大的情况下,已测试):
如果增加元素一直是使用add()(增加到末尾)的话,那是ArrayList要快
一直删除末尾的元素也是ArrayList要快【不用复制移动位置】
至于如果删除的是中间的位置的话,还是ArrayList要快。
但一般来说:增删多还是用LinkedList,因为上面的情况是极端的。