集合底层源码分析之List(六)
前言
本章节讲解ArrayList和LinkedList,ArrayList是数组结构,继承AbstractList,实现List、RandomAccess、Cloneable、Serializable 接口;LinkedList是链表结构,继承AbstractSequentialList,实现List、Deque、Cloneable、Serializable接口。
源码分析
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 = {};
//默认空数组容器
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//用于存放元素列表的数组
transient Object[] elementData; // non-private to simplify nested class access
//元素大小
private int size;
//Interger最大值减8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//传入初始化容器的有参构造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {//判断初始化容器大于0
this.elementData = new Object[initialCapacity];//创建一个指定大小的容器赋值给elementData数组
} else if (initialCapacity == 0) {//如果初始化容器大小等于0
this.elementData = EMPTY_ELEMENTDATA;//elementData等于空数组
} else { //否则抛异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//无参构造elementData等于默认空数组且容器容量等于10
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//有参构造传入集合元素
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//转换为数组赋值给elementData
if ((size = elementData.length) != 0) {//判断元素大小等于数组长度且不等于0
// 判断class不相等
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);//elementData的容器容量等于集合的容器大小
} else {// 否则替换为空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
//获取元素数量
public int size() {
return size;
}
//判断元素是否为0
public boolean isEmpty() {
return size == 0;
}
}
ensureCapacityInternal()方法源码分析
//指定容器大小
private void ensureCapacityInternal(int minCapacity) {
//判断element数组等于默认空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//比较默认容器大小和传入的容器大小获取最大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
ensureExplicitCapacity()方法源码分析
//保证容器大小
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//操作次数累加
// 判断传入的容器大小减数组容器大小大于0
if (minCapacity - elementData.length > 0)
grow(minCapacity);//调用grow方法
}
grow()方法源码分析
//增加容量
private void grow(int minCapacity) {
// 获取数组长度
int oldCapacity = elementData.length;
//新容器大小等于旧容器大小加旧容器大小的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)//判断新容器大小减传入的容器大小小于0
newCapacity = minCapacity;//新容器大小等于传入容器大小
if (newCapacity - MAX_ARRAY_SIZE > 0)//判断新容器大小减最大容器大小大于0
newCapacity = hugeCapacity(minCapacity);//获取最大容器
// 数组容器大小等于新容器大小
elementData = Arrays.copyOf(elementData, newCapacity);
}
hugeCapacity()方法源码分析
//超大容器
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) //判断容器参数小于0
throw new OutOfMemoryError();//抛出异常
//容器参数大于最大数组容器,返回Integer最大值,否则返回最大数组容器
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
rangeCheckForAdd()方法源码分析
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)//判断下标大于元素大小或元素小于0抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
fastRemove()方法源码分析
private void fastRemove(int index) {//通过下标删除
modCount++;//操作次数累加
int numMoved = size - index - 1;//计算移动的元素个数
if (numMoved > 0)//判断移动的元素个数大于0
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);//数组复制
elementData[--size] = null; // size减一后size位的下标位设为null
}
add()方法源码分析
新增有两个方法,一个按顺序插入,一个按指定位置插入,分别进行讲解
//顺序插入
public boolean add(E e) {
ensureCapacityInternal(size + 1);//容器
elementData[size++] = e;//数组容器大小累加且对应下标赋值
return true;
}
//指定容器大小
private void ensureCapacityInternal(int minCapacity) {
//判断element数组等于默认空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//比较默认容器大小和传入的容器大小获取最大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//保证容器大小
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//操作次数累加
// 判断传入的容器大小减数组容器大小大于0
if (minCapacity - elementData.length > 0)
grow(minCapacity);//调用grow方法
}
//增加容量
private void grow(int minCapacity) {
// 获取数组长度
int oldCapacity = elementData.length;
//新容器大小等于旧容器大小加旧容器大小的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)//判断新容器大小减传入的容器大小小于0
newCapacity = minCapacity;//新容器大小等于传入容器大小
if (newCapacity - MAX_ARRAY_SIZE > 0)//判断新容器大小减最大容器大小大于0
newCapacity = hugeCapacity(minCapacity);//获取最大容器
// 数组容器大小等于新容器大小
elementData = Arrays.copyOf(elementData, newCapacity);
}
案例讲解
第二次插入
然后再来讲解指定位置插入
//指定位置添加
public void add(int index, E element) {
rangeCheckForAdd(index);//校验数组越界
ensureCapacityInternal(size + 1);//获取容器大小
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//数组复制
elementData[index] = element; //数组容器大小累加且对应下标赋值
size++;//元素累加
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)//判断下标大于元素大小或元素小于0抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//指定容器大小
private void ensureCapacityInternal(int minCapacity) {
//判断element数组等于默认空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//比较默认容器大小和传入的容器大小获取最大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//保证容器大小
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//操作次数累加
// 判断传入的容器大小减数组容器大小大于0
if (minCapacity - elementData.length > 0)
grow(minCapacity);//调用grow方法
}
//增加容量
private void grow(int minCapacity) {
// 获取数组长度
int oldCapacity = elementData.length;
//新容器大小等于旧容器大小加旧容器大小的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)//判断新容器大小减传入的容器大小小于0
newCapacity = minCapacity;//新容器大小等于传入容器大小
if (newCapacity - MAX_ARRAY_SIZE > 0)//判断新容器大小减最大容器大小大于0
newCapacity = hugeCapacity(minCapacity);//获取最大容器
// 数组容器大小等于新容器大小
elementData = Arrays.copyOf(elementData, newCapacity);
}
最后再来讲解ArrayList是如何扩容的
add()方法:小结
- ArrayList的添加元素,如果没给定初始化容器大小,第一次进来,默认分配数组容器大小为10,每次操作累计元素个数。当插入的元素大于数组容器大小时,就会进行扩容,扩容大小为1.5倍。
- 指定位置添加元素,要保证元素个数大于下标位置,否则会抛异常。通过将指定下标元素(包括下标后的元素)往后挪动的方式,去替换当前下标的元素
get()方法源码分析
//获取元素 index=数组下标
public E get(int index) {
rangeCheck(index);//校验是否越界
return elementData(index);//返回下标对应的元素
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)//判断下标大于元素大小或元素小于0抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];//通过下标获取数组元素
}
案例讲解
set()方法源码分析
//修改元素 index=下标,element=替换元素
public E set(int index, E element) {
rangeCheck(index);//校验是否越界
E oldValue = elementData(index);//获取下标节点
elementData[index] = element;//替换元素
return oldValue;//返回原元素
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)//判断下标大于元素大小或元素小于0抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];//通过下标获取数组元素
}
案例讲解
get()和set()方法:小结
- get()和set() 方法都很简单,由于底层是数组结构,获取和替换元素都只需要通过下标就能很快速的操作
remove()方法源码分析
删除也分为两种,一种是通过值删除,另一种通过下标删除。一般来说,删除元素都是通过值来删除,因为你无法确认你删除的元素在什么位置,由源码可知,删除效率不高。
//删除元素
public boolean remove(Object o) {
if (o == null) {//判断o等于null
for (int index = 0; index < size; index++)//从0开始遍历size次
if (elementData[index] == null) {//判断每次遍历的值等于null
fastRemove(index);//调用删除方法
return true;//返回true
}
} else {//否则o不等于null
for (int index = 0; index < size; index++)//从0开始遍历size次
if (o.equals(elementData[index])) {//判断传入的值等于每次遍历的值
fastRemove(index);//调用删除方法
return true;//返回true
}
}
return false;//返回false
}
private void fastRemove(int index) {//通过下标删除
modCount++;//操作次数累加
int numMoved = size - index - 1;//计算移动的元素个数
if (numMoved > 0)//判断移动的元素个数大于0
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);//数组复制
elementData[--size] = null; // size减一后size位的下标位设为null
}
案例讲解
再来讲解按照下标删除
//删除指定下标元素
public E remove(int index) {
rangeCheck(index);//校验是否越界
modCount++;//操作次数累计
E oldValue = elementData(index);//通过下标获取值
int numMoved = size - index - 1;//计算移动的元素个数
if (numMoved > 0)//判断移动的元素个数大于0
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);//数组复制
elementData[--size] = null;// size减一后size位的下标位设为null
return oldValue;//返回值
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)//判断下标大于元素大小或元素小于0抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];//通过下标获取数组元素
}
remove()方法:小结
- ArrayList的删除也是非常的简单,删除有两种方式,一种通过值进行删除,一种通过下标删除。
- 通过值删除。首先通过遍历获取值对应的下标,通过将指定下标后的元素往前挪动的方式,去删除最后一个元素,通过值删除返回的ture或false,false表示没找到对应的下标。
- 通过下标删除。和通过值删除差不多,通过将指定下标后的元素往前挪动的方式,去删除最后一个元素,省去了遍历的操作,不过要保证元素个数大于下标位置,否则会抛异常。通过下标删除返回的是下标的值,如果没有返回的是null
LinkedList主要属性及构造方法分析
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
//元素大小默认0
transient int size = 0;
//头节点
transient Node<E> first;
//尾节点
transient Node<E> last;
//无参构造函数
public LinkedList() {
}
//有参构造
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
private static class Node<E> {//node节点
E item;//当前节点的值
Node<E> next;//下一个节点
Node<E> prev;//上一个节点
Node(Node<E> prev, E element, Node<E> next) {//构造方法
this.item = element;//给item赋值参数element
this.next = next;//给item的下一个节点赋值
this.prev = prev;//给item的上一个节点赋值
}
}
}
linkLast()方法源码分析
//尾部插入
void linkLast(E e) {
final Node<E> l = last;//获取最后一个节点
final Node<E> newNode = new Node<>(l, e, null);//创建新节点且和最后一个节点建立关系
last = newNode;//last节点等于新的节点
if (l == null)//判断l节点等于null
first = newNode;//first节点等于新节点
else //否则l节点不等于null
l.next = newNode;//l节点的下一个节点等于新节点(奖励双向关系)
size++;//元素个数累加
modCount++;//操作次数累加
}
checkPositionIndex()方法源码分析
private void checkPositionIndex(int index) {//校验是否越界
if (!isPositionIndex(index))//调用判断方法如果返回false,抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
isPositionIndex()方法源码分析
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;//判断节点位置大于等于0且小于等于元素个数
}
node()方法源码分析
Node<E> node(int index) {//通过节点位置获取节点
// assert isElementIndex(index);
if (index < (size >> 1)) {//判断节点位置小于元素个数的一半
Node<E> x = first;//获取头节点
for (int i = 0; i < index; i++)//从0开始正序遍历index次
x = x.next;//x节点等于x节点的下一个节点
return x;//返回x节点
} else { // 否则节点位置大于元素个数的一半
Node<E> x = last;//获取尾节点
for (int i = size - 1; i > index; i--)//从尾部开始倒叙遍历
x = x.prev;//x节点等于x节点的上一个节点
return x;//返回x节点
}
}
linkBefore()方法源码分析
void linkBefore(E e, Node<E> succ) {//前插入方法
// assert succ != null;
final Node<E> pred = succ.prev;//获取节点的上一个节点
final Node<E> newNode = new Node<>(pred, e, succ);//创建新节点建立上下关系
succ.prev = newNode;//succ节点的上一个节点等于新节点(建立双向关系)
if (pred == null)//判断上一个节点等于null
first = newNode;//first节点等于新节点
else //否则上一个节点不等于null
pred.next = newNode;
上一个节点的下一个节点等于新节点
size++;//元素个数累加
modCount++;//操作次数累加
}
checkElementIndex()方法源码分析
private void checkElementIndex(int index) {
if (!isElementIndex(index))//结果为false,抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
isElementIndex()方法源码分析
private boolean isElementIndex(int index) {
return index >= 0 && index < size; //判断节点位置大于等于0且小于等于元素个数
}
unlink()方法源码分析
//解除
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;//获取x节点的值
final Node<E> next = x.next;//获取x节点的下一个节点
final Node<E> prev = x.prev;//获取x节点的上一个节点
if (prev == null) {//判断上一个节点等于null
first = next;//头节点的下一个节点
} else {//否则上一个节点不等于null
prev.next = next;//上一个节点的下一个节点等于x节点的下一个节点
x.prev = null;//将x节点的上一个节点设为null
}
if (next == null) {//判断下一个节点等于null
last = prev;//尾节点等于上一个节点
} else {//否则下一个节点不等于null
next.prev = prev;//下一个节点的上一个节点等于x节点的上一个节点
x.next = null;//x节点的下一个节点设为null
}
x.item = null;//将x节点的值设为null
size--;//元素个数减一
modCount++;//操作次数累加
return element;//返回对应的值
}
add()方法源码分析
LinkedList也是一样有两种插入方式,下面分别进行讲解
//添加元素
public boolean add(E e) {
linkLast(e);//调用尾部插入方法
return true;//返回true
}
//尾部插入
void linkLast(E e) {
final Node<E> l = last;//获取最后一个节点
final Node<E> newNode = new Node<>(l, e, null);//创建新节点且和最后一个节点建立关系
last = newNode;//last节点等于新的节点
if (l == null)//判断l节点等于null
first = newNode;//first节点等于新节点
else //否则l节点不等于null
l.next = newNode;//l节点的下一个节点等于新节点(奖励双向关系)
size++;//元素个数累加
modCount++;//操作次数累加
}
案例讲解
//指定位置插入
public void add(int index, E element) {
checkPositionIndex(index);//校验是否越界
if (index == size)//判断插入的节点位置等于元素个数
linkLast(element);//调用尾部插入方法
else //否则节点位置不等于元素个数
linkBefore(element, node(index));//获取对应节点后调用linkBefore()插入方法
}
private void checkPositionIndex(int index) {//校验是否越界
if (!isPositionIndex(index))//调用判断方法如果返回false,抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;//判断节点位置大于等于0且小于等于元素个数
}
Node<E> node(int index) {//通过节点位置获取节点
// assert isElementIndex(index);
if (index < (size >> 1)) {//判断节点位置小于元素个数的一半
Node<E> x = first;//获取头节点
for (int i = 0; i < index; i++)//从0开始正序遍历index次
x = x.next;//x节点等于x节点的下一个节点
return x;//返回x节点
} else { // 否则节点位置大于元素个数的一半
Node<E> x = last;//获取尾节点
for (int i = size - 1; i > index; i--)//从尾部开始倒叙遍历
x = x.prev;//x节点等于x节点的上一个节点
return x;//返回x节点
}
}
void linkBefore(E e, Node<E> succ) {//前插入方法
// assert succ != null;
final Node<E> pred = succ.prev;//获取节点的上一个节点
final Node<E> newNode = new Node<>(pred, e, succ);//创建新节点建立上下关系
succ.prev = newNode;//succ节点的上一个节点等于新节点(建立双向关系)
if (pred == null)//判断上一个节点等于null
first = newNode;//first节点等于新节点
else //否则上一个节点不等于null
pred.next = newNode;
上一个节点的下一个节点等于新节点
size++;//元素个数累加
modCount++;//操作次数累加
}
add()方法:小结
- LinkedList的添加元素,由于是链表结构,第一个元素为头节点,其他情况向尾部添加元素即可
- 指定位置添加元素,稍微比较复杂,先判断插入的节点位置是否等于元素个数,如果等于则尾部插入即可,否则,要先去获取指定位置的节点,不过获取节点这也做了优化,判断节点是否大于元素个数的一半,如果小于一半从上往下开始查找,大于元素个数的一半,从最后一个节点开始往上查找,这样遍历的速度就会快很多,获取完节点,然后在获取上一个节点,使其获取的节点及上一个节点于插入的节点建立双向关系。
get()方法源码分析
//获取值
public E get(int index) {
checkElementIndex(index);//校验是否越界
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))//结果为false,抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size; //判断节点位置大于等于0且小于等于元素个数
}
Node<E> node(int index) {//通过节点位置获取节点
// assert isElementIndex(index);
if (index < (size >> 1)) {//判断节点位置小于元素个数的一半
Node<E> x = first;//获取头节点
for (int i = 0; i < index; i++)//从0开始正序遍历index次
x = x.next;//x节点等于x节点的下一个节点
return x;//返回x节点
} else { // 否则节点位置大于元素个数的一半
Node<E> x = last;//获取尾节点
for (int i = size - 1; i > index; i--)//从尾部开始倒叙遍历
x = x.prev;//x节点等于x节点的上一个节点
return x;//返回x节点
}
}
案例讲解
set()方法源码分析
//修改节点值
public E set(int index, E element) {
checkElementIndex(index);//校验节点位置是否越界
Node<E> x = node(index);//获取节点位置的节点
E oldVal = x.item;//获取节点的值
x.item = element;//将节点的值替换为传入的参数
return oldVal;//返回原节点的值
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))//结果为false,抛异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size; //判断节点位置大于等于0且小于等于元素个数
}
Node<E> node(int index) {//通过节点位置获取节点
// assert isElementIndex(index);
if (index < (size >> 1)) {//判断节点位置小于元素个数的一半
Node<E> x = first;//获取头节点
for (int i = 0; i < index; i++)//从0开始正序遍历index次
x = x.next;//x节点等于x节点的下一个节点
return x;//返回x节点
} else { // 否则节点位置大于元素个数的一半
Node<E> x = last;//获取尾节点
for (int i = size - 1; i > index; i--)//从尾部开始倒叙遍历
x = x.prev;//x节点等于x节点的上一个节点
return x;//返回x节点
}
}
get()和set()方法:小结
- get()方法通过节点的位置获取节点的值,判断节点的位置是否大于元素个数的一半,如果小于一半从上往下开始查找,大于元素个数的一半,从最后一个节点开始往上查找,这样遍历的次速度就会快很多。
- 既然已经知道了get()方法,set()方法修改就很简单,将获取的节点对应的值替换为要修改的值。
remove()方法源码分析
//通过值删除节点
public boolean remove(Object o) {
if (o == null) {//判断值等于null
for (Node<E> x = first; x != null; x = x.next) {//从first节点开始遍历
if (x.item == null) {//判断x节点的值等于null
unlink(x);//调用删除方法
return true;//返回true
}
}
} else {//否则值不等于null
for (Node<E> x = first; x != null; x = x.next) {/从first节点开始遍历
if (o.equals(x.item)) {//判断参数值等于x节点的值
unlink(x); //调用删除方法
return true; //返回true
}
}
}
return false; //返回false
}
//解除
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;//获取x节点的值
final Node<E> next = x.next;//获取x节点的下一个节点
final Node<E> prev = x.prev;//获取x节点的上一个节点
if (prev == null) {//判断上一个节点等于null
first = next;//头节点的下一个节点
} else {//否则上一个节点不等于null
prev.next = next;//上一个节点的下一个节点等于x节点的下一个节点
x.prev = null;//将x节点的上一个节点设为null
}
if (next == null) {//判断下一个节点等于null
last = prev;//尾节点等于上一个节点
} else {//否则下一个节点不等于null
next.prev = prev;//下一个节点的上一个节点等于x节点的上一个节点
x.next = null;//x节点的下一个节点设为null
}
x.item = null;//将x节点的值设为null
size--;//元素个数减一
modCount++;//操作次数累加
return element;//返回对应的值
}
案例讲解
//通过节点位置删除
public E remove(int index) {
checkElementIndex(index);//校验是否越界
return unlink(node(index));//先获取节点后删除
}
Node<E> node(int index) {//通过节点位置获取节点
// assert isElementIndex(index);
if (index < (size >> 1)) {//判断节点位置小于元素个数的一半
Node<E> x = first;//获取头节点
for (int i = 0; i < index; i++)//从0开始正序遍历index次
x = x.next;//x节点等于x节点的下一个节点
return x;//返回x节点
} else { // 否则节点位置大于元素个数的一半
Node<E> x = last;//获取尾节点
for (int i = size - 1; i > index; i--)//从尾部开始倒叙遍历
x = x.prev;//x节点等于x节点的上一个节点
return x;//返回x节点
}
}
//解除
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;//获取x节点的值
final Node<E> next = x.next;//获取x节点的下一个节点
final Node<E> prev = x.prev;//获取x节点的上一个节点
if (prev == null) {//判断上一个节点等于null
first = next;//头节点的下一个节点
} else {//否则上一个节点不等于null
prev.next = next;//上一个节点的下一个节点等于x节点的下一个节点
x.prev = null;//将x节点的上一个节点设为null
}
if (next == null) {//判断下一个节点等于null
last = prev;//尾节点等于上一个节点
} else {//否则下一个节点不等于null
next.prev = prev;//下一个节点的上一个节点等于x节点的上一个节点
x.next = null;//x节点的下一个节点设为null
}
x.item = null;//将x节点的值设为null
size--;//元素个数减一
modCount++;//操作次数累加
return element;//返回对应的值
}
remove()方法:小结
- LinkedList删除方法也是非常简单的,有两种方式删除:通过值删除和通过节点位置删除,两个都需要先遍历获得节点,不过通过节点位置删除遍历速度要快些,然后调用删除方法,节点删除也是操作起来很简单。如果删除的是第一个节点,那么下一个节点成为头节点;如果是最后一个节点,上一个节点成为尾节点;如果是中间节点,就让上一个节点和下一个节点建立连接即可。最后把删除节点的连接关系设为null。
知识扩展
看完整个源码,也就知道他们之间的异同点,这里就不再过多的讲解
章节目录
上一篇:集合底层源码分析之Set(五)