一、链表简介
链表:是一种链式的存储结构(像火车一样) 为了解决增加和删除的效率 不便于查询 (和数组相对应)
单链表: 只能从表头开始逐一访问,查询不便 (常见面试题:反转? 如何查询倒数第k个节点?)
双向链表: 增加了一个前置指针(上一个位置) 可以从任一节点开始访问其他节点
节点三要素: 数据、前置指针、后置指针
定义出了首节点和尾节点 首节点的前置指针为null (头节点,串联所有节点的,本身没有数据) 尾节点的后置指针为null
环形链表: 首尾相连 LinkedList在jdk1.6前是环形链表 (常见面试题:约瑟夫环)
二、LinkedList源码
1 、双向链表的特性
a 节点 Node(内部类) ,前置位置prev 后置位置next b 首节点first 尾节点last c 没有大小限制
2 、源码分析
a) 类声明
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
i 继承 AbstractSequentialList 以及 实现List接口, 代表有增删改查方法
ii 实现 Deque 双向队列
iii 实现Cloneable ,可以克隆
iv 实现Serializable, 可以被序列化
b) 构造函数
i 无参构造器 ii 有参构造器
// 可以将集合转化为链表 批量增加
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
c) 增加元素
public boolean add(E e) {
//默认从尾部连接节点
linkLast(e);
return true;
}
//节点三要素
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<E> last;
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;
// 长度加1
size++;
// 更改次数+1
modCount++;
}
d) 查询元素
public E get(int index) {
// 先校验索引的有效性
checkElementIndex(index);
// 根据索引找到节点 返回数据
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 判断索引范围是否在[0,size)之间
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
// 提升查询效率??
// 判断索引在前半段还是在后半段 决定从哪儿开始遍历
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;
}
}