目录
一、LinkedList内部结构
二、LinkedList类开头的定义
三、transient关键字修饰的变量
四、无参构造和有参构造方法
五、介绍几个比较常用操作的方法
首先LinkedList内部结构如下,其内部每一个元素都指向下一个元素:
┌───┬───┐ ┌───┬───┐ ┌───┬───┐ ┌───┬───┐
HEAD ──>│ A │ ●─┼──>│ B │ ●─┼──>│ C │ ●─┼──>│ D │ │
└───┴───┘ └───┴───┘ └───┴───┘ └───┴───┘
观察其内部结构可知,LinkedList添加删除元素时,无需移动元素,仅需改变部分结点指向的next值即可。
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable
首先观察LinkedList类开头的定义可知,LinkedList继承自AbstractSequentialList泛型类,实现了List、Deque(双端)、Cloneable(克隆接口)、Serializable接口(序列化接口)。
AbstractSequentialList继承自AbstractList
然后利用transient关键字定义了如下三个变量:
transient int size = 0;//列表大小
transient Node first;//头结点
transient Node last;//尾结点
被transient关键字修饰的变量,在LinkedList类实现序列化是被自动忽略,序列化的目的是将该类写入数据库或文件中以便持久化保存,或者用于网络传输。而我们这里的列表大小、头结点、尾结点是随着创建不同列表的改变而而动态变化的,不需要写入磁盘长久保存,仅在调用时会用到,所以用transient修饰只作用于内存,不写入磁盘。
接着定义了无参构造和有参构造方法,有参构造方法利用了Java的泛型机制,可传入任意类型一组对象,并调用addAll()方法将所传的这组对象全部添加进具体的LinkedList实例化对象中。
public LinkedList() {//无参构造方法
}
public LinkedList(Collection extends E> c) {//有参构造方法
this();
addAll(c);
}
这里又不得不先提到addAll()方法了
LinkedList类中定义了返回值为boolean类型的allALL()方法
//LinkedList中addAll()方法
public boolean addAll(Collection extends E> c) {
return addAll(size, c);
}
返回值调用的值另一个addAll()方法,如下
//具体实现方法addAll()
public boolean addAll(int index, Collection extends E> c) {
checkPositionIndex(index);//检查索引位置合不合法
Object[] a = c.toArray();//将c转换成Object类型的数组a
int numNew = a.length;//定义numNew为数组a的长度
if (numNew == 0)//判断,若长度为0,说明未传入任何元素
return false;//返回false
Node pred, succ;//定义前驱结点pred,和当前结点succ
if (index == size) {//若索引==列表大小
succ = null; //succ置为空
pred = last;//将尾结点last赋给pred,从最后开始添加
} else {//否则
succ = node(index);//根据索引找到当前结点
pred = succ.prev;//当前节点的前驱指向pred,保存信息
}
for (Object o : a) {//遍历该数组
@SuppressWarnings("unchecked") E e = (E) o;//下转
Node newNode = new Node<>(pred, e, null);创建新结点传入前驱,当前元素值
if (pred == null)//如果pred前驱为空,说明为头一个添加
first = newNode;//该结点即为头结点
else //否则
pred.next = newNode;//前一个结点的next指向新创建结点
pred = newNode; //前一个节点变为新结点,以便接着添加
}
if (succ == null) {//如果当前结点为空
last = pred;//前驱结点为尾结点
} else {//否则
pred.next = succ;//前驱结点的next指向当前结点
succ.prev = pred;//当前结点的前驱prev指向前驱结点
}
size += numNew;//原本列表大小更新原本列表大小+加上所添加的元素个数
modCount++;
return true;//说明添加成功
}
其中要提的是checkPositionIndex(index)方法,该方法检查索引合法不合法会调用到下面这个isPositionIndex(index)方法:
private boolean isPositionIndex(int index) {//判断位置是否合法
return index >= 0 && index <= size;//返回索引值是否在0-列表大小范围内
}
1.remove() 删除元素方法
public boolean remove(Object o) {
if (o == null) {//如果元素为null
//从头结点开始遍历
for (Node x = first; x != null; x = x.next) {
if (x.item == null) {//找到当前结点的值为空的结点
unlink(x);//调用Unlink()删除结点x
return true;//返回true,删除成功
}
}
} else {//元素不是null
//依旧从头结点开始遍历
for (Node x = first; x != null; x = x.next) {
if (o.equals(x.item)) {//如果当前结点的值等于要删除的值
unlink(x);调用Unlink()删除结点x
return true;//返回true,删除成功
}
}
}
return false;
}
相关的 unlink() 方法(删除结点方法)
E unlink(Node x) {
// assert x != null;
final E element = x.item;//存放当前结点元素的值
//根据x->next找到当前结点的下个结点next
final Node next = x.next;
//根据x->prev找到当前结点的上个结点prev
final Node prev = x.prev;
//若上个结点为空,说明当前结点为第一个结点
if (prev == null) {
first = next;//让下一个结点赋给头结点
} else {//否则为中间结点
//上个结点的后继next值指向当前结点的下个节点
prev.next = next;
x.prev = null;//当前结点的前驱prev置为空
}
//若下个结点为空,说明当前结点为最后一个结点
if (next == null) {
last = prev;//将上一个结点赋给尾结点
} else {
//下一个节点的前驱指向当前结点的上个结点
next.prev = prev;
x.next = null;//当前结点的后继next置为空
}
x.item = null;//x的值置为空
size--;//大小-1
modCount++;
return element;//返回元素值
}
2.getLast() 方法:得到尾结点
//得到尾结点方法
public E getLast() {
//l被final修饰,不可改变
final Node l = last;//尾结点赋给结点l
if (l == null) //结点l为空
//抛出没有该元素异常
throw new NoSuchElementException();
//否则,返回结点的item值
return l.item;
}
getFirst () :得到头结点方法与此类似。
3. add () 添加方法: 默认添加到尾部
public boolean add(E e) {
linkLast(e);//调用添加到尾部方法
return true;//添加成功
}
linkLast()实现机制类似unlink()方法。
4.set() 方法:根据索引将元素添加到指定位置
public E set(int index, E element) {
checkElementIndex(index);//检查索引位置的合法性
Node x = node(index);//根据索引创建对应的结点x
E oldVal = x.item; //将x的值赋给oldVal
x.item = element; //将所要添加的元素值赋给当前结点的item
return oldVal;//返回结点值
}
get() 方法: 根据索引拿到指定位置的元素
public E get(int index) {
checkElementIndex(index);//检查元素索引值的合法性
return node(index).item;//返回根据索引找到的结点值
}
5.isElementIndex():判断元素索引在不在列表范围内
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
6.indexOf(): 根据元素值返回对应下标
public int indexOf(Object o) {
int index = 0;
if (o == null) {//若元素值为空
//从头遍历结点
for (Node x = first; x != null; x = x.next) {
if (x.item == null)//比较,若当前结点的值为空
return index;//返回其下标
index++;//index加加
}
} else {//元素值不为空
//从头遍历
for (Node x = first; x != null; x = x.next) {
if (o.equals(x.item))//比较,若当前结点值等于传入值
return index;//返回当前下标
index++;
}
}
return -1;//否则没找到,返回-1
}
LinkedList类中还有很多方法,期待下次和大家分享。