文章目录
List集合
list有序集合一般分为三种实现类包括ArrayList、Vector、LinkedList。
一、ArrayList的实现原理
最常用的List实现类,底层是动态数组,允许所有的元素插入,包括null,每个元素中间不允许有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高,所以它适合随机查找(get)和替换(set),不适合插入(add)和删除(remove)。同时,线程是不安全的。
1.1 属性
//默认容量的大小
private static final int DEFAULT_CAPACITY = 10;
//空数组常量
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认的空数组常量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMEN
//存放元素的数组,从这可以发现 ArrayList 的底层实现就是一个 Object数组
transient Object[] elementData;
//数组中包含的元素个数
private int size;
//数组的最大上限
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
1.2 构造方法
elementData 起始是一个大小为 0 的空数组,指定初始大小后,new出此大小的数组。
若未指定大小时,是通过无参构造器创建大小为0的ArrayList,当使用到时(添加第一个元素),通过grow方法创建大小为10的数组。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity]; //new出元素数组
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
} }
//无参构造器
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
1.3 get方法
.ArrayList的get 方法先是判断一下索引有没有越界,之后就直接通过数组下标来获取元素,所以 get 的时间复杂
度是 O(1)。
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
1.4 set方法
set 方法的作用是把下标为 index 的元素替换成新的元素 element,与get类似,时间复杂度是 O(1)。
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
1.5 add方法
不带指定下标的add方法:在插入元素之前,它会先检查是否需要扩容,然后再把元素添加到数组中最后一个元素的后面。
时间复杂度是 O(1)。
带下标index的add方法:检查是否需要扩容后,调用系统的arraycopy方法,把 index 位置开始的元素都往后挪一位,再添加到指定位置,时间复杂度为O(n)。
1.6 remove方法
与add方法类似,也是调用系统的 arraycopy 方法将index 位置开始的元素都往前挪一位,时间复杂度为 O(n)。
1.7 grow方法
grow 方法是在数组进行扩容的时候用到的,ArrayList 每次扩容都是扩 1.5 倍,然后调用 Arrays 类的 copyOf 方法,把元素重新拷贝到一个新的数组中。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//拷贝到新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
1.8 size方法
时间复杂度是 O(1),直接返回数组中元素的个数,而不是数组的实际大小。
1.9 indexOf方法(lastIndexOf)
返回第一个等于给定元素的值的下标。从头往前通过遍历比较数组中每个元素的值来查找的,所以它的时间复杂度是 O(n)。
lastIndexOf 的原理跟 indexOf 一样,仅仅是从后往前找起。
二、Vector的实现原理
与ArrayList大致一样,只不过在方法中加入synchronized 来保证线程安全。
不同点:
1、Vector 每次扩容都以当前数组大小的 2 倍去扩容。当指定了 capacityIncrement 之 后,每次扩容仅在原先基础上增加 capacityIncrement 个单位空间。
2、Vector 创建时的默认大小为 10,并且在初始化就已经创建了数组,不像ArrayList初始为0,使用到时才创建。
三、LinkedList的实现原理
LinkedList 是通过一个双向链表来实现的,它允许插入所有元素,包括 null,并且提供了 List 接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。相比数组,链表的特点就是在指定位置插入和删除元素的效率较高(只需遍历整个链表,而不像ArrayList一样需要对数组进行复制、移动,代价较小,相对效率较高),但是查找的效率就不如数组那么高(需要遍历链表,可以从尾部或者头部开始,而数组查找不需要)。同时,它是线程不同步的。
双向链表结构:
3.1 属性
//链表的节点个数
transient int size = 0;
//指向头节点的指针
transient Node<E> first;
//指向尾节点的指针
transient Node<E> last;
3.2 常用方法
//删除表头元素
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
//删除表尾元素
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
//插入新的表头节点
public void addFirst(E e) {
linkFirst(e);
}
//插入新的表尾节点
public void addLast(E e) {
linkLast(e);
}
//链表的大小
public int size() {
return size;
}
//添加元素到表尾
public boolean add(E e) {
linkLast(e);
return true;
}
//删除指定元素
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false; }
//获取指定下标的元素
public E get(int index) {
checkElementIndex(index); //先检查是否越界
return node(index).item;
}
//替换指定下标的值
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
//在指定位置插入节点
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//删除指定下标的节点
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
//获取表头节点的值,表头为空返回 null
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//获取表头节点的值,表头为空抛出异常
public E element() {
return getFirst();
}
//获取表头节点的值,并删除表头节点,表头为空返回 null
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//添加元素到表头
public void push(E e) {
addFirst(e);
}
//删除表头元素
public E pop() {
return removeFirst();
}