一、概念:
LinkedList ,基于节点实现的双向链表的 List ,每个节点都指向前一个和后一个节点从而形成链表。
相比 ArrayList 来说,我们日常开发使用 LinkedList 相对比较少。
二、实现类:
LiskedList 实现了 List<E>, Deque<E>, Cloneable, java.io.Serializable 接口,
与ArrayList实现相比少了一个java.util.RandomAccess 接口,也就是不支持随机访问。
但是比ArrayList多了一个Deque<E> 接口,提供了双端队列的实现,因此再遍历链表头和尾部的时候效率比较高。
三、属性:
transient int size = 0;//size 记录元素的个数
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;//first 指针 指向的是第一个Node节点
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;// last指针 指向的是最后一个Node节点
Node 节点的实现。
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;
}
}
四、构造函数:
public LinkedList() {
}
//构造方法可以看出LinkedList没有容量这一说. 也就不存在扩容这一说.
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
五、添加单个元素:
public boolean add(E e) {
//队尾添加元素
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;//原来的last节点
final Node<E> newNode = new Node<>(l, e, null);//第一个参数是前一个节点是l,后一个节点是null
last = newNode; // last 指向新节点,
if (l == null) //如果原来的last节点为null, 说明此时链表为空,
first = newNode;//那么让第一个指针也指向新节点, 此时first 指向的是第一个节点,
else
l.next = newNode;//原来的last节点的下一个节点指向newNode
//增加链表大小
size++;
//增加数组修改次数
modCount++;
}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element); // 指定位置为列表最后一个位置直接插入
else
//先调用node(index) 找到第index 位置的Node节点node,
linkBefore(element, node(index)); // 遍历找到指定位置的元素,并在该元素前插入指定元素
}
//遍历 所有node节点
Node<E> node(int index) {
// assert isElementIndex(index);
//索引都说从0开始的.
if (index < (size >> 1)) { // index位置在前半部分从前向后遍历
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { // index位置在后半部分从后向前遍历
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
private void linkFirst(E e) {
final Node<E> f = first; // 记录第一个node
final Node<E> newNode = new Node<>(null, e, f); // 新建node,prev为null,next为之前记录的第一个node,item为传入的参数e
first = newNode; // 第一个node指向新建的node:newNode
if (f == null) //原来的first节点为null 此时为空list
last = newNode; // 如果记录的第一个node为null,说明原来的list为空list,将last也执行新建的node
else
f.prev = newNode; // 否则原来的first node的prev指向新建node
size++; // list的size+1
modCount++; // 结构修改记录数+1
}
还支持addFirst(E e)队头,addLast(E e)队尾;push(E e)队头,offer(E e)队尾;
六、添加多个元素:
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index); // 检查指定位置是否越界
Object[] a = c.toArray(); // 将给定集合转换成数组
int numNew = a.length; // 获取给定集合长度(需要添加的元素个数)
if (numNew == 0)
return false; //不需要添加元素
//pred 记录的是原来index位置前面的节点,succ 记录的是原来index位置上的节点,
Node<E> pred, succ;
if (index == size) { // 指定的插入位置为list最后
succ = null; // 当前元素设置为null
pred = last; // prev设置为last (原来的last节点)
} else {
succ = node(index); // 遍历链表查出当前node,这里需要遍历链表,因此相对耗时
pred = succ.prev; // prev设置为当前node的prev (记录的是原来的index位置上的前一个节点,)
}
for (Object o : a) { // 循环要加入的数组
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null); // 新建node,pred指向前一个node,item为当前数组元素的值,next指向null
if (pred == null)
first = newNode; // 说明原链表为空链表,first指向新建node
else
pred.next = newNode; // 否则前面node的next指向新建node
pred = newNode; // pred改为指向新建node
}
if (succ == null) { // 在链表的最后插入新集合
last = pred; // last设置为新集合的最后一个元素
} else {
pred.next = succ; // 否则新插入集合的最后一个元素node的next指向succ
succ.prev = pred; // succ的prev指向新插入集合最后一个元素
}
size += numNew; // size增加集合大小
modCount++;
return true;
}
七、移除单个元素:
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item; // 记录f的item值
final Node<E> next = f.next; // 记录f的next node
f.item = null; // f的item至空
f.next = null; // help GC // f的next至空
first = next; // first指向原来第一个node的下一个node
if (next == null)
last = null; // 如果next为空说明list为空列表,last也至空 表里没元素了
else
next.prev = null; // 否则next node的prev至空
size--; // list元素大小-1
modCount++;
return element; // 返回解除的元素值
}
//移除队尾 元素
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;//说明没有元素了
else
prev.next = null;// l 的next指针指向 null
size--;
modCount++;
return element;
}
//移除某个位置上的元素
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));//通过node(index) 查找Node节点, 然后进行移除.
}
//移除指定元素。
public boolean remove(Object o) {
if (o == null) { // 如果参数给定的对象为null,则从前向后遍历链表,当查找到某个node的item为null时将该node从链表中移除,并返回true
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else { // 如果参数给定的对象不为null,则从前向后表里链表,当查找到某个node的item equals给定对象的值,则将该node从链表中移除,并返回true
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false; // 没有找到指定对象,返回false
}
八、移除多个元素:
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
//迭代器遍历然后移除。
九、查找单个元素:
public int indexOf(Object o) {
int index = 0;
if (o == null) { // 指定对象为null时从前向后遍历,找到null返回指定位置
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else { // 指定对象不为null时从前向后遍历,找到对象返回指定位置
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
public E get(int index) {
checkElementIndex(index); // 检查指定位置是否越界
return node(index).item; //遍历元素
}
Node<E> node(int index) {
// assert isElementIndex(index);
//索引都说从0开始的.
if (index < (size >> 1)) { // index位置在前半部分从前向后遍历
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { // index位置在后半部分从后向前遍历
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
十、设置指定位置上的元素:
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);//遍历node节点,找到节点元素。
E oldVal = x.item;
x.item = element; // 设置指定位置的元素为新值
return oldVal; // 返回旧值
}
十一、转换成数组:
public Object[] toArray() {
Object[] result = new Object[size];//创建数组
int i = 0;
//遍历节点
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
//如果传入的数组小于size 大小 反射创建一个新的数组
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
//遍历 链表 ,复制到a中
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)//如果传入的数组大小大于size, 则将size 赋值为null
a[size] = null;
return a;
}
十二、清空链表:
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
for (Node<E> x = first; x != null; ) { // 遍历链表 让他的指针都断开,并且附上元素的值为null
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
十三、迭代器:
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
public boolean hasNext() {
return nextIndex < size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
......
}
LinkedList 的迭代器遍历遍历是要比 for循环遍历效率高的。
总结:
-
LinkedList基于节点实现的双向链表的List,每个节点都指向前一个和后一个节点从而形成链表。
-
LinkedList提供队列、双端队列、栈的功能
-
LinkedList 随机访问平均时间复杂度是 O(n) ,查找指定元素的平均时间复杂度是 O(n) 。
-
LinkedList 移除指定位置的元素的最好时间复杂度是 O(1) ,最坏时间复杂度是 O(n) ,平均时间复杂度是 O(n) 。
-
LinkedList 添加元素的最好时间复杂度是O(1),最坏时间复杂度是O(n),平均时间复杂度是O(n)。
最后 LinkedList 和 Redis 的List 数据结构有点类似。