标签:双向链表 检验 lin cti deque lse amp 没有 开始
一、概述:
LinkedList 与 ArrayList 一样实现 List 接口,只是 ArrayList 是 List 接口的大小可变数组的实现,LinkedList 是 List 接口链表的实现。基于链表实现的方式使得 LinkedList 在插入和删除时更优于 ArrayList,而随机访问则比 ArrayList 逊色些。
二、源码分析:
2.1 定义方法:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
从这段代码中我们可以清晰地看出 LinkedList 继承 AbstractSequentialList,实现 List、Deque、Cloneable、Serializable。其中 AbstractSequentialList 提供了 List 接口的骨干实现,从而最大限度地减少了实现受“连续访问”数据存储(如链接列表)支持的此接口所需的工作,从而以减少实现 List 接口的复杂度。
2.2 属性
transient int size = 0;//元素个数 /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ transient Node<E> first;//头结点 /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ transient Node<E> last;//尾节点
2.3 内部类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; } }
Node为 LinkedList 的内部类,它定义了存储的元素。该元素的前一个元素、后一个元素,这是典型的双向链表定义方式
2.4 构造方法
public LinkedList() {
} public LinkedList(Collection<? extends E> c) { this(); addAll(c);//调用addAll()方法,下面详解 }
2.5 添加方法
linkedList中存在着许多添加方法,这里找几个典型方法进行剖析
2.5.1 add():将指定元素添加到此列表的结尾
public boolean add(E e) { linkLast(e); return true; }
上面显示add方法调用了linkLast(E e) 方法,我们点进去看
void linkLast(E e) { //得到最后一个节点 final Node<E> l = last; //创建一个新的节点,将原尾节点设置此节点的前置节点,上面Node内部类有参入传入解析 final Node<E> newNode = new Node<>(l, e, null); //让新节点成为最后的节点 last = newNode; //如果原尾节点是空的,则代表原来没有元素节点,则头节点也是新插入节点 if (l == null) first = newNode; //如果不是空的,原尾节点的后置节点便是新插入节点,因为是双向链表 else l.next = newNode; //元素节点个数+1 size++; //统计修改次数 modCount++; }
2.5.2 add(int index, E element) :在指定未知插入节点
public void add(int index, E element) { //检验下标越界 checkPositionIndex(index); //如果下标等于大小,则执行linkList,上面已经分析过 if (index == size) linkLast(element); //如果不相等,则执行linkBefore else linkBefore(element, node(index)); }
在进入linkBefore之前,我们首先进入node()方法中进行解析
node(int index):用来查找 index 位置的节点元素。
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; } }
下面我们进入linkBefore(E e, Node<E> succ) 中去看
void linkBefore(E e, Node<E> succ) { // assert succ != null; //e 要插入的节点 succ 插入位置原有的节点 //获取要插入位置的前置节点,然后创建新节点,传参 final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); //将succ的前置节点设置为新节点 succ.prev = newNode; //如果succ前置节点为空,则此时新节点为头结点 if (pred == null) first = newNode; //否则将前置节点的后置节点设置为新节点 else pred.next = newNode; size++; modCount++; }
2.5.3 addAll(Collection<? extends E> c) :将 Collection 中的所有元素添加到列表中
public boolean addAll(Collection<? extends E> c) { return addAll(size, c); } public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index); Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Node<E> pred, succ; if (index == size) { succ = null; pred = last; } else { succ = node(index); pred = succ.prev; } for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node<E> newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; } if (succ == null) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; }
这里面经过上面两个方法的介绍,其实也不难理解,即将collection中的元素转换成Object数组,然后再赋值,赋值也是调用上面解析的方法。当然还有各色各样的添加方法,可以直接添加头,添加尾,其道理都一样
2.6 删除
2.6.1 remove(int index):删除指定下标的元素节点
public E remove(int index) { checkElementIndex(index); return unlink(node(index)); }
这里主要是unlink(Node<E> x)方法,进去剖析
E unlink(Node<E> x) { // assert x != null; //当前节点,前置节点,后置节点 final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; //如果前置节点为空,那么后置节点变为头结点,否则前置节点的后置节点便是当前节点的后置节点 if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } //同理 if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
2.6.2 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()方法 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; }
删除方法也有很多方法,比如删除头,删除尾等,不过多解释
2.7 查询方法 get(int index)
public E get(int index) { checkElementIndex(index);
//node(index)方法,上面已经解析过 return node(index).item; }
标签:双向链表 检验 lin cti deque lse amp 没有 开始
原文地址:https://www.cnblogs.com/xiao-ran/p/11869725.html