一、概述
● 底层原理:双向链表(动态数据结构)
● 特点:
①有序可重复(包含null 值),可通过索引检索。
②具有高效的插入和删除操作,但在随机访问时效率较低。
③可以用作栈(stack)和队列(queue)这两种数据结构的底层实现
④非线程安全,如果需要在多线程环境中使用链表,并保证线程安全性,可以采取以下两种方式,使用同步机制:可以使用 synchronized 关键字或其他线程安全的同步机制来保护对 LinkedList 的并发访问,例如:List<String> synchronizedList = Collections.synchronizedList(new LinkedList<>());
或者使用线程安全的替代类:例如 CopyOnWriteArrayList、ConcurrentLinkedDeque
等。
二、双向链表数据结构
三、向LinkedList对象中添加元素
● boolean add(E e):
将指定元素添加到列表末端,等价于addLast(E e)
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); //创建一个新的节点 newNode,将元素 e 存储在该节点中,并将其前驱节点指向 l,后继节点指向 null
last = newNode; // 重新赋值
if (l == null)
first = newNode; //链表之前为空,将新节点设置为头节点
else
l.next = newNode; //之前的最后一个节点 l 不为空,则将其后继节点指向新节点
size++;
modCount++; //修改计数器的值
}
// 内部类
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;
}
}
● void add(int index, E element)
:指定索引位置添加元素
public void add(int index, E element) {
checkPositionIndex(index); //校验索引是否越界,索引小于 0 或者大于链表的大小
if (index == size)
linkLast(element); //将元素添加到列表尾部
else
linkBefore(element, node(index)); //在列表中间添加元素
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev; //succ的前一个节点
final Node<E> newNode = new Node<>(pred, e, succ); //创建新节点,新节点的pre就是succ的pre节点,新节点的next就是succ节点
succ.prev = newNode; //succ.pre指向新节点
if (pred == null)
first = newNode; //新节点为头节点
else
pred.next = newNode; //succ的上一节点的next指向新节点
size++;
modCount++;
}
四、向LinkedList对象中删除元素
● boolean remove(Object o)
用于从链表中删除指定的元素
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;
}
● E remove(int index):
移除该列表中指定位置的元素
public E remove(int index) {
checkElementIndex(index); //校验索引是否越界
return unlink(node(index)); //调用该方法将该节点从链表中删除
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item; //将节点 x 的元素保存到变量 element 中,用于最后返回被删除的元素
final Node<E> next = x.next; //获取节点 x 的后一个节点 next
final Node<E> prev = x.prev; //获取节点 x 的前一个节点 prev
if (prev == null) {
first = next; //节点 x 是链表的头节点,则将链表的 first 指针指向 next,表示删除了头节点。
} else {
prev.next = next; // prev 的 next 指针指向 next,跳过节点 x,使得链表仍然连续
x.prev = null;
}
if (next == null) {
last = prev; //节点 x 是链表的尾节点,则将链表的 last 指针指向 prev,表示删除了尾节点
} else {
next.prev = prev; //将 next 的 prev 指针指向 prev,跳过节点 x,使得链表仍然连续。
x.next = null;
}
x.item = null; //将节点 x 的元素设为 null,以帮助垃圾回收器回收内存
size--;
modCount++;
return element;
}
五、对LinkedList对象中修改元素
● E set(int index, E element):
修改此列表中当前索引元素的值
public E set(int index, E element) {
checkElementIndex(index); //校验索引是否越界
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
六、获取LinkedList对象中的元素
● E get(int index)
:获取此列表中当前索引位置元素的值
public E get(int index) {
checkElementIndex(index); //校验索引是否越界
return node(index).item;
}
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;
}
}