LinkedList
以双向链表实现,链表无容量限制,但是双向链表本身就使用了更多的空间,也需要额外的链表指针操作.
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;
}
}
先说一下Node
节点,LinkedList
是以双向链表的形式实现,每个节点都由Node
组成,node
包含有当前节点的元素,以及上一个元素和下一个元素的Node
值
LinkedList#add(E e)
这里我们同样以add为切入点
LinkedList#add()
// LinkedList的成员变量 size 代表链表的长度,元素的数量
transient int size = 0;
// 链表的首个节点
transient Node<E> first;
// 链表的最后一个节点
transient Node<E> last;
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++;
}
主要是linkedLast(E e)
,首先得到链表的最后一个节点,并根据传入的元素创建一个新的节点,并将原先最后的节点作为新创建节点的上一个节点,新建创建的节点的下一个节点为设置为null,将新创建的节点赋值给last
,即:新创建的节点作为最后一个节点,如果原始的尾节点为空,则将头结点first
也赋值newNode
即新创建的节点,如果原始的尾节点不为空,则将原始尾节点的下一个节点指针指向新创建的节点newNode
,然后size
自增1,修改次数modCount
自增1。
set(int index,E element)
ArrayList#set():
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
set()
中首先根据指定的下标得到相应的Node值。来看node()
中的判断,如果指定的指针小于链表长度的一半,则从头节点开始进行遍历,如果大于链表的一半,则从尾节点开始进行遍历,然后找到指定指针,并返回该指针所对应的node值。最后将遍历到的node中的元素值修改为指定的元素值,并返回原始元素值。
get(int index)
get()
比较简单,这里不再多说
public E get(int index) {
checkElementIndex(index);
// 根据node()获取到指定下标的node值,并返回包含的元素值
return node(index).item;
}
add(int index, E element)插入方法
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
里面有一个判断条件,如果要插入的指针等于链表的长度,即在链表的尾部插入一个节点,则直接调用linkLast()
即可,如果不等于,则调用linkBefore()
,其中入参为指定的元素element
和指定节点上的node
;
LinkedList#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;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
首先获取到指定节点succ
的上一个节点pred
,并根据传入的元素创建一个新的节点newNode
,并将指定节点succ
的上一个节点pred
作为新创建节点newNode
的上一个节点,查询到的指定节点succ
为新创建节点newNode
的下一个节点,并将获取到的指定节点succ
的上一个节点的指针指向新创建的节点newNode
,指定节点的上一个节点pred
的下一个节点指针指向新创建的节点,最后链表长度加一,修改次数加一 (emmmmmmmmmm有点绕口)
remove(int index)
ArrayList#remove():
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
checkElementIndex()
为检查index是否合法,这里不再多说,unlink
的入参为指定指针所对应node值,获取到指定的node值之后,在获取该node多对应的元素值element
,上一个节点prev
,下一个节点next
,然后再分别对上下节点做判断:(可能还是会有点绕口 )
如果上一个节点
prev
等于null,说明此节点为头结点,赋值给first
节点,如果上一个节点prev
不为null,则将上一个节点prev
的的下一个节点指针指向next节点,并将当前节点x
的上一个节点指针指向null
如果下一个节点next
为null,说明当前节点为尾节点,赋值给last
,如果下一个节点next
不为null,则将下一个节点next
的上一个节点指针指向上一个节点prev
,当前节点x
的下一个节点指针指向null.
关于LinkedList的其他方法,这里就不在多说了。
Iterator
LinkedList
间接继承了AbstractList
,其中有默认实现的Itr
迭代器,其实ArrayList
同样继承了AbstractList
,不知道为什么没有直接调用,用法通ArrayList一样(具体的可以看List(一):ArrayList源码浅析的Iteraotr小节):
LinkedList<String> arrayList = new LinkedList<>();
arrayList.add("chuan--0");
arrayList.add("chuan--1");
arrayList.add("chuan--2");
arrayList.add("chuan--3");
arrayList.add("chuan--4");
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()){
String str = iterator.next();
Log.i(TAG, "onResume: " + str);
AbstractList
中的Itr
类,有三个成员变量, 四个方法,以LinkedList为例
cursor 当前节点所对应的下一个节点的下标
lastRet 迭代的最后一次的节点所对应的下标
expectedModCount 根据此属性判断迭代期间是否有其他操作,如果有会抛出异常
hasNext() 判断是否有下一个节点
nex() 获取下一个节点
remove 删除当前节点
public boolean hasNext() {
// 获取到当前链表的总长度,即元素总数量
return cursor != size();
}
public E next() {
checkForComodification();
try {
// 注意 此时的cursor代表当前节点的指针,下面会有一个cursor+1的操作,那时的cursor代表的是下一个节点的指针
int i = cursor;
// 获取到下一个节点
E next = get(i);
// 将当前节点的指针下标赋值给lastRet
lastRet = i;
cursor = i + 1;
// 返回当前节点
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
// 直接调用remove方法 进行操作
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
// 此时将lastRet赋值为-1,即每次next只能进行一次删除操作
lastRet = -1;
// 同步expectedModeCount,保证修改次数的一致性
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
ListItr
ListItr
继承于Itr
和ListIterator
Listerator
有几个抽象方法
add() 插入节点
next() 当前节点的下一个节点
hasPrevious() 当前节点时候是否有前一个节点,即当前节点是否是头结点
hasNext() 当前节点是否有尾节点,即当前节点时候是尾节点
nextIndex() 当前节点的下一节点的下标
previousIndex() 当前节点的上一节点的下标
remove() 删除当前节点
set() 修改当前节点
Itr
前面有说过,主要负责遍历链表和删除操作.
来看ListItr
,其包含有四个成员变量和几个重写方法,主要是重写的ListIterator
中的抽象方法
lastReturned 遍历的最后一次返回的node值,即当前节点的node值
next 当前节点的下一节点
nextIndex 当前节点的下一节点的下标
expectedModCount 集合修改的次数,用于判断遍历期间是否有发生修改链表的操作
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
这是ListItr
的构造器,入参为节点的index值,并获取当前节点
LinkedList#ListItr#hasNext():
public boolean hasNext() {
return nextIndex < size;
}
判断当前节点是否有下一节点,表现为当前节点的下标是否小于链表的长度
LinkedList#ListItr#next()
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
获取当前节点的下一个节点值,并吧当前节点值赋值给lastReturned
,next重新赋值为next的下一节点值,nextIndex
自增1,并返回lastReturned
的元素
// 获取当前节点的下一节点的下标值
public int nextIndex() {
return nextIndex;
}
// 获取当前节点的上一节点的下标值
public int previousIndex() {
return nextIndex - 1;
}
// 判断当前节点是否有上一节点,即当前节点是否为头结点
public boolean hasPrevious() {
return nextIndex > 0;
}
LinkedList#ListItr#previous()
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
此方法是为了获取当前节点的的上一节点值,并将下一节点下标值nextIndex
减一
LinkedList#ListItr#remove
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
删除当前节点,首先获取到当前节点的下一节点值,并通过unlink()
进行节点重新关联操作,然后将下一节点下标nextIndex
减一,expectedModeCount
进行操作次数加一.
LinkedList#ListItr#set():
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
修改操作比较简单这里不再多说
LinkedList#ListItr#add()
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
插入操作,如果当前节点为null,即插入为新增操作或者空链表进行插入操作,会通过linkLast()
进行尾部节点插入操作,否则通过linkBefore()
进行插入操作,下一节点下标值自增1,修改次数自增1