java学习小结:list集合实现原理


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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值