LinkedList 是链表实现的线性表(双链表),元素有序且可以重复。
LinkedList继承AbstractSequentialList并且实现了List和Deque,Cloneable, Serializable接口。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
1.属性
//链表元素的个数
transient int size = 0;
//第一个元素的指针
transient Node<E> first;
//最后一个元素的指针
transient Node<E> last; Node 类,这是 LinkedList 类中的一个内部类,集合里每一个元素就代表一个 Node 类对象,LinkedList 集合就是由许多个 Node 对象类似于手拉着手构成,由此可知,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;
}
}
每个节点都prev都保存上一个节点的引用,next保存下一个节点的引用;
2.构造函数
- 无参构造函数
public LinkedList() {
} - 泛型参数有参构造函数
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
} 3.添加元素
- 添加元素
public boolean add(E e) {
//将元素添加到链表的尾部
linkLast(e);
return true;
}
void linkLast(E e) {
//用变量将链表的尾节点存储下来
final Node<E> l = last;
//新增加一个元素,即新增加一个node节点。该节点prev指向当前last节点,e为添加元素,next指向null
final Node<E> newNode = new Node<>(l, e, null);
//将链表尾部指向新节点
last = newNode;
if (l == null)
//如果链表为空,将这个新节点也设为头部节点,那么新增节点既是头节点也是尾节点,并且头节点的prev和next都是null
first = newNode;
else
//链表不为空,将之前存储的原链表尾部节点的next指向新增节点
l.next = newNode;
//节点数加1
size++;
modCount++;
}
这里只介绍add(E e),addFirst(E e),addLast(E e)和 实现类似。
- 将元素插入指定位置
public void add(int index, E element) {
//判断索引是否越界
checkPositionIndex(index);
if (index == size)
//如果索引值等于链表大小,则直接在链尾添加元素
linkLast(element);
else
linkBefore(element, node(index));
}
//根据索引获取节点,因为是链表,不像数组,在内存中并不是一块连续的位置存储,不能根据索引直接取到值,需要从头部或者尾部一个个向下找
Node<E> node(int index) {
//size >> 1表示移位,指除以2的1次方
if (index < (size >> 1)) {//如果索引比链表的一半小
Node<E> x = first;//设x为头节点,表示从头节点开始遍历
for (int i = 0; i < index; i++)//因为只需要找到index处,所以遍历到index处就可以停止
x = x.next;//从第一个节点开始向后移动,直到移动到index前一个节点就能找到index处的节点
return x;
} else {//如果索引比链表的一半大
Node<E> x = last;//设x为尾部节点,表示从最后一格节点开始遍历
for (int i = size - 1; i > index; i--)
x = x.prev;//从最后一个节点开始向前移动,直到移动到index后一个节点就能找到index处的节点
return x;
}
}
void linkBefore(E e, Node<E> succ) {
//获取index 节点的上一个节点
final Node<E> pred = succ.prev;
//新增一个节点,prev指向pred,e为新加元素,next指向succ节点
final Node<E> newNode = new Node<>(pred, e, succ);
//succ的prev指向新节点
succ.prev = newNode;
//如果插入节点的上一个节点引用为空
if (pred == null)
//头部节点设为新节点
first = newNode;
else
//原始index的上一个节点的next 设为新节点
pred.next = newNode;
//链表长度+1
size++;
modCount++;
} 4.修改元素
public E set(int index, E element) {
//判断索引是否越界
checkElementIndex(index);
Node<E> x = node(index);//获取指定索引处的节点
E oldVal = x.item;//获取指定索引处节点的实际元素
x.item = element;//将指定位置的节点元素替换成需要修改的元素
return oldVal;//返回索引处原来的节点的元素值
} 5.查找元素
public E get(int index) {
//判断索引是否越界 return index >= 0 && index <= size;
checkElementIndex(index);
//node(index)上面已经讲过,这里获取节点的实际元素
return node(index).item;
} 返回链表中第一个出现指定元素的索引
public int indexOf(Object o) {
int index = 0;
if (o == null) {//查找的元素为null
for (Node<E> x = first; x != null; x = x.next) {//从头结点开始不断向下一个节点进行遍历
if (x.item == null)
return index;//找到了则返回index
index++;
}
} else {//查找的元素不为null
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))//使用equals 比较
return index;//找到了则返回index
index++;
}
}
return -1;//找不到指定元素,则返回-1
} 6.删除元素
public E remove(int index) {
checkElementIndex(index);
//先获取指定位置的节点
return unlink(node(index));
}
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {//如果删除节点的上一个节点引用为null,表示删除节点为头节点
first = next;//将头节点设置为删除节点的下一个节点
} else {
prev.next = next;//将删除节点的上一个节点的next指向删除节点的下一个节点
x.prev = null;//将删除节点的上一个节点引用设为null,否则链表就乱了
}
if (next == null) {//如果删除节点的下一个节点引用为null,表示删除节点为尾部节点
last = prev;//将尾部节点设为删除节点的上一个节点
} else {//不是尾部节点
next.prev = prev;//将删除节点的下一个节点的prev指向删除节点的上一个节点
x.next = null;//将删除节点的下一个节点引用设为null
}
x.item = null;//将删除节点的实际元素设为null,便于垃圾回收
size--;
modCount++;
return element;
}
版权声明:本文为Tan_xiong原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。