源码分析——LinkedList

本文详细分析了LinkedList的源码,探讨了其与ArrayList和Vector的区别。LinkedList继承AbstractSequentialList并实现Deque接口,使其具备链表、队列、栈等多种功能。其成员变量、构造器、增删改查等操作的实现原理被逐一揭示,突出其在不同场景下的适用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继续分析List的整体框架结构:

从上图可以看出,LinkedList与ArrayList和Vector不同,它直接继承的父类是AbstractSequentialList而不是AbstractList。 我们再来看一看它的体系结构:

public class LinkedList<E> extends AbstractSequentialList<E>
		implements List<E>,Deque<E>,Cloneable,java.io.Serializable

从上图可以看出,LinkedList实现的接口除了ArrayList和Vector都实现过的List、Cloneable和Serializeable接口外,还实现了一个Deque接口,那来分析分析其父类AbstractSequentialList类和额外接口Deque到底有什么作用,为什么要继承这个父类和实现这个额外接口。

首先来分析一下AbstractSequentialList和AbstractList的区别,上源码。

public void add(int index, E element) {
        throw new UnsupportedOperationException();
}

public E remove(int index) {
        throw new UnsupportedOperationException();
}

public E set(int index, E element) {
        throw new UnsupportedOperationException();
}

 abstract public E get(int index);

简单截取一部分AbstractList的增删改查代码,发现四个方法都没有给出具体实现,如何实现都是让其子类或者子类的子类自己决定。

再来看AbstractSequentialList:

public void add(int index, E element) {
        try {
            listIterator(index).add(element);
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
}


public E remove(int index) {
        try {
            ListIterator<E> e = listIterator(index);
            E outCast = e.next();
            e.remove();
            return outCast;
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
}


public E set(int index, E element) {
        try {
            ListIterator<E> e = listIterator(index);
            E oldVal = e.next();
            e.set(element);
            return oldVal;
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
}


public E get(int index) {
        try {
            return listIterator(index).next();
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
}

从上述源代码可以看出,AbstractSequentialList将增删改查都基本实现了一遍,而且都使用的是双向迭代器进行实现,也就是说继承了AbstractSequentialList这个父类,就要实现ListIterator这个接口,并且用迭代器来实现增删改查操作。从这里我们就能清楚了,以这种规定限制了继承AbstractSequentialList的子类只能是链表这样的数据结构了。因为顺序表也使用这种迭代的方式进行增删改查会极大的降低效率。

接下来再看一看新实现的Deque

public interface Deque<E> extends Queue<E> {

    void addFirst(E e);

    void addLast(E e);

    boolean offerFirst(E e);

    boolean offerLast(E e);

    E removeFirst();

    E removeLast();

    E pollFirst();

    E pollLast();

    E getFirst();

    E getLast();

    E peekFirst();

    E peekLast();

    boolean removeFirstOccurrence(Object o);

    boolean removeLastOccurrence(Object o);

    boolean add(E e);

    boolean offer(E e);

    E remove();

    E poll();

    E element();

    E peek();

    void push(E e);

    E pop();

    boolean remove(Object o);

    boolean contains(Object o);

    public int size();

    Iterator<E> iterator();

    Iterator<E> descendingIterator();

}

从上面可以看出Deque是队列Queue的一个子接口,也被称为双向队列,是一个集大成者的数据结构,既可以实现先进后出模式的队列,也可以实现先进先出的栈,还可以实现顺序表中的某些方法,因此Deque的增删改查都对应着多种方法,分别用于不同的场景。所以实现了Deque接口的ArrayList也有着这么多的功能,既可以从头部插入或删除,也可以从尾部插入和删除。因此功能十分强大。

好了,框架结构基本梳理完了,接下来看LinkedList的成员变量:

transient int size = 0;
//设置链表的长度,不可序列化
transient Node<E> first;
//设置链表的第一个结点,不可序列化
transient Node<E> last;
//设置链表的最后一个结点,不可序列化

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;
	}
}

LinkedList成员变量有三个,分别存储着链表的长度,链表的第一个结点和链表的最后一个结点,且三个成员变量都使用transient修饰,即表明该类对象序列化时不包括这些成员变量。此处还要介绍一下链表的底层存储结构Node,如上面代码所示,Node是LinkedList的私有静态内部类,其成员变量有三个,分别是本结点的内容item,下一个结点next,上一个结点prev,这两个也就相当于指针的,分别作为前置指针和后置指针,将链表中所有的结点联系起来。因此LinkedList的底层数据结构应该是双向链表。

再继续看一下LinkedList的构造器:

public LinkedList(){
//构造器1,空参构造器
}
			
public LinkedList(Collection<? extends E> c){
//构造器2,传入一个集合进行构造链表
	this();
        addAll(c);
}

LinkedList构造器只有两个,一个空参不做任何其他处理,另外一个传入一个集合,则首先调用空参构造器构造一个空的链表,再调用addAll方法将集合c插入到空链表中。因此链表最初始的时候应该是空的,当加入第一个元素进来时,它就创建一个结点将该元素存储下来,然后first和last都指向该元素,再从尾部添加一个元素时,链表再创建一个Node结点将元素保存,然后last指向新结点,从而形成了一个简单链表。

好,接下来上其余源码:

public class LinkedList<E> extends AbstractSequentialList<E>
		implements List<E>,Deque<E>,Cloneable,java.io.Serializable
		{
			transient int size = 0;
			//设置链表的长度,不可序列化
			transient Node<E> first;
			//设置链表的第一个结点,不可序列化
			transient Node<E> last;
			//设置链表的最后一个结点,不可序列化
			
			public LinkedList(){
				//构造器1,空参构造器
			}
			
			public LinkedList(Collection<? extends E> c){
				//构造器2,传入一个集合进行构造链表
				this();
				addAll(c);
			}
			
			private void linkFirst(E e){
				//在链表头部插入一个元素
				final Node<E> f = first;//将链表原头部取出来
				final Node<E> newNode = new Node<>(null, e, f);//创建一个新的结点
				first = newNode;//将结点设置为头部
				if(f == null)//如果原头结点为空,说明链表为空
					last = newNode;//将尾结点也设置为新结点
				else
					f.prev = newNode;//否则设置原头结点的前置指针指向新结点
				size++;//链表长度增加一
				modCount++;
			}
			
			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;
				size++;
				modCount++;
			}
			
			void linkBefore(E e, Node<E> succ){
				//在结点succ之前插入一个结点
				final Node<E> pred = succ.prev;//获取succ之前的结点
				final Node<E> newNode = new Node<>(pred, e, succ);//创建新结点
				succ.prev = newNode;//将succ前置指针指向新结点
				if(pred == null)//如果原前结点为空,说明succ为链表头部结点
					first = newNode;//故将新结点设置为头结点
				else
					pred.next = newNode;//否则将原前结点的后置指针指向新结点
				size++;//链表长度增加一
				modCount++;
			}
			
			private E unlikFirst(Node<E> f){
				//删除头结点
				final E element = f.item;//获取头结点中的内容
				final Node<E> next = f.next;//获取头结点后一个结点
				f.item = null;//将头结点设置为空,方便回收
				f.next = null;//将头结点的后置指针设置为空,方便回收
				first = next;//将后一个结点设置为头结点
				if(next == null)//如果后一个结点为空,说明此时链表为空,设置尾指针为空
					last = null;
				else
					next.prev = null;//否则设置后一个结点的前置指针为空
				size--;//链表长度减一
				modCount++;
				return element;//返回被删除的结点的内容
			}
			
			private E unlinkLast(Node<E> l){
				//删除尾结点,和删除头结点基本一致,注意
				//两个方法都是私有的,不能公共调用
				final E element = l.item;
				final Node<E> prev = l.prev;
				l.item = null;
				l.prev = null;
				last = prev;
				if(prev == null)
					first = null;
				else
					prev.next = null;
				size--;
				modCount++;
				return element;
			}
			
			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)//如果前结点为空,那说明该结点是头结点
					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;//返回被删除结点的内容
			}
			
			public E getFirst(){
				//获得链表头结点的内含元素
				final Node<E> f = first;
				if(f == null)
					throw new NoSuchElementException();
				return f.item;
			}
			
			public E getLast(){
				//获取链表尾结点的内含元素
				final Node<E> l = last;
				if(l == null)
					throw new NoSuchElementException();
				return l.item;
			}
			
			public E removeFirst(){
				//移除链表第一个结点,调用的unlinkFirst方法,
				//主要是检查了该结点的安全性,即验证结点是否为空
				final Node<E> f = first;
				if(f == null)
					throw new NoSuchElementException();
				return unlinkFirst(f);
			}
			
			public E removeLast(){
				//和移除头结点类似,移除链表的尾结点
				final Node<E> l = last;
				if(l == null)
					throw new NoSuchElementException();
				return unlinkLast(l);
			}
			
			public void addFirst(E e){
				//在链表头部插入结点,对外方法
				linkFirst(e);
			}
			
			public void addLast(E e){
				//在链表尾部插入结点,对外方法
				linkLast(e);
			}
			
			public boolean contains(Object o){
				//查询链表中是否含有某元素
				return indexOf(o) != -1;
			}
			
			public int size(){
				//获得链表长度
				return size;
			}
			
			public boolean add(E e){
				//在链表尾部插入结点,如果成功返回true
				linkLast(e);
				return true;
			}
			
			public boolean remove(Object o){
				//移除某一个结点,该结点的内容是o
				//此处应用for循环进行链表遍历,强,从没想过
				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;
			}
			
			public boolean addAll(Collection<? extends E> c){
				//在链表末尾增加一个集合c
				return addAll(size,c);
			}
			
			public boolean addAll(int index, Collection<? extends E> c){
				//在某一个索引结点后面插入一个集合c
				checkPositionIndex(index);
				//检查索引的合法性
				Object[] a = c.toArray();
				//将集合转变为数组
				int numNew = a.length;
				//获得数组的长度
				if(numNew == 0)
					return false;
				//如果数组长度为0,无法插入,返回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结点
					succ.prev = pred;//并且将succ节点的前置指针指向最后一个元素结点
				}
				size += numNew;//增加链表长度
				modCount++;
				return true;//成功插入,返回
			}
			
			public void clear(){
				//清空链表中的所有结点,即将所有的结点都释放
				for(Node<E> x = first; x != null; ){
					Node<E> next = x.next;
					x.item = null;
					x.next = null;
					x.prev = null;
					x = next;
				}
				first = last = null;
				size = 0;
				modCount++;
			}
			
			public E get(int index){
				//获得索引中的结点内的内容
				checkElementIndex(index);
				//核查索引是否合法
				return node(index).item;
				//此处使用的是node的方法,即先判断索引的大小
				//如果比链表的长度的一半小,则从前遍历寻找
				//否则从链表的末尾开始向前寻找,提高的效率
			}
			
			public E set(int index, E element){
				//修改某个索引指向的结点的信息
				checkElementIndex(index);
				Node<E> x = node(index);
				E oldVal = x.item;
				x.item = element;
				return oldVal;
			}
			
			public void add(int index, E element){
				//在某个索引所指的结点前面添加一个结点
				checkPositionIndex();
				//核查索引是否合法,此处方法和checkElementIndex方法
				//的不同之处在于此处索引可以是链表的长度而c方法不可以
				if(index == size)
					linkLast(element);
				else
					linkBefore(element,node(index));
			}
			
			public E remove(int index){
				//移除索引所指向的链表结点
				checkElementIndex(index);
				return unlink(node(index));
			}
			
			private boolean isElementIndex(int index){
				//判断索引是否在链表索引的范围内
				return index >= 0 && index < size;
			}
			
			private boolean isPositionIndex(int index){
				//判断索引是否在链表索引的范围内,与上一个
				//方法的不同之处是可以包含链表的长度的
				return index >= 0 && index <= size;
			}
			
			private String outOfBoundsMsg(int index){
				//设置异常提示信息
				return "Index: " + index + ", Size" + size;
			}
			
			private void checkElementIndex(int index){
				//抛出索引越界异常,为何要分开写,而不直接写在一起??????
				if(!isElementIndex(index))
					throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
			}
			
			private void checkPositionIndex(int index){
				//抛出索引越界异常
				if(!isPositionIndex(index))
					throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
			}
			
			Node<E> node(int 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;
				}
			}
			
			public int indexOf(Object o){
				//根据内容查找链表中第一个出现该元素的结点的索引
				int index = 0;
				if(o == null){
					for(Node<E> x = first; x != null; x = x.next){
						if(x.item == null)
							return index;
						index++;
					}
				}else{
					for(Node<E> x = first; x != null; x = x.next){
						if(o.equals(x.item))
							return index;
						index++;
					}
				}
				return -1;
			}
			
			public int lastIndexOf(Object o){
				//根据元素逆序查找链表中最后一次出现该元素的结点的索引
				int index = size;
				if(o == null){
					for(Node<E> x = last; x != null; x = x.prev){
						index--;
						if(x.item == null)
							return index;
					}
				}else{
					for(Node<E> x = last; x != null; x = x.prev){
						index--;
						if(o.equals(x.item))
							return index;
					}
				}
				return -1;
			}
			
			public E peek(){
				//类似于栈的取栈顶元素,此处也是取链表头部结点中的元素
				//但是不会移除头结点,且当头结点为空时返回空,不会抛出异常
				//此处实现栈中的方法
				final Node<E> f = first;
				return (f == null)
						? null
						: f.item;
			}
			
			public E element(){
				//也是取头结点元素,但是如果头结点为空时会抛出异常
				return getFirst();
			}
			
			public E poll(){
				//取出链表头结点元素并且删除头结点,即头结点向后移动
				//如果头结点为空则返回空,不抛出异常
				final Node<E> f = first;
				return (f == null) ? null : unlikFirst(f);
			}
			
			public E remove(){
				//取出链表头结点并删除头结点
				//如果头结点为空则抛出无此元素异常
				return removeFirst();
			}
			
			public boolean offer(E e){
				//offer是队列中的方法,即快速插入元素,此处调用add进行插入
				//此处实现双向队列中的方法
				return add(e);
			}
			
			public boolean offerFirst(E e){
				//将元素插入到链表头部
				addFirst(e);
				return true;
			}
			
			public boolean offerLast(E e){
				//将元素插入到链表尾部
				addLast(e);
				return true;
			}
			
			public E peekFirst(){
				//此处实现双向队列中的方法
				final Node<E> f = first;
				return (f == null)
						? null
						: f.item;
			}
			
			public E peekLast(){
				//双向队列中取出尾部元素,不删除
				final Node<E> l = last;
				return (l == null)
						? null
						: l.item;
			}
			
			public E pollFirst(){
				//双向队列中取出头部元素并删除头部结点
				final Node<E> f = first;
				return (f == null)
						? null
						: unlinkFirst(f);
			}
			
			public E pollLast(){
				//双向队列中取出尾部元素并删除尾部结点
				final Node<E> l = last;
				return (l == null)
						? null
						: unlinkLast(l);
			}
			
			public void push(E e){
				//实现栈中压栈操作
				addFirst(e);
			}
			
			public E pop(){
				//实现栈中弹栈操作
				return removeFirst();
			}
			
			public boolean removeFirstOccurrence(Object o){
				//删除第一次出现的某元素,如果该元素不存在,则不做任何动作
				return remove(o);
			}
			
			public boolean removeLastOccurrence(Object o){
				//删除最后一次出现的某元素,如果该元素不存在,则不做任何动作
				if(o == null){
					for(Node<E> x = last; x != null; x = x.prev){
						if(x.item == null){
							unlink(x);
							return true;
						}
					}
				}else{
					for(Node<E> x = last; x != null; x = x.prev){
						if(o.equals(x.item)){
							unlink(x);
							return true;
						}
					}
				}
				return false;
			}
			
			public ListIterator<E> listIterator(int index){
				//获得列表迭代器,此处就没有普通的构造器了
				checkPositionIndex(index);
				return new ListItr(index);
			}
			
			private class ListItr implements ListIterator<E>{
				private Node<E> lastReturned;//记录当前的结点
				private Node<E> next;//记录下一个结点
				private int nextIndex;//记录下一个结点索引
				private int expectedModCount = modCount;
				
				ListItr(int 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;//返回当前结点的元素
				}
				
				public boolean hasPrevious(){
					//判断是否有上一个结点
					return nextIndex > 0;
				}
				
				public E previous(){
					//遍历上一个结点,原理相同
					checkForComodification();
					if(!hasPrevious())
						throw new NoSuchElementException();
					lastReturned = next = (next == null)
										? last
										: next.prev;
					nextIndex--;
					return lastReturned.item;
				}
				
				public int nextIndex(){
					//获得下一个结点索引值
					return nextIndex;
				}
				
				public int previousIndex(){
					//获得上一个结点索引值,为何只要减一就行了???
					return nextIndex - 1;
				}
				
				public void remove(){
					//移除当前结点
					checkForComodification();
					if(lastReturned == null)
						throw new IllegalStateException();
					Node<E> lastNext = lastReturned.next;
					unlink(lastReturned);
					if(next == lastReturned)//如果原结点不是尾节点,则将原结点的下一个结点赋值给next
						next = lastNext;
					else
						nextIndex--;//否则如果原结点是尾节点,那么直接将索引减一即可
					lastReturned = null;//将原结点释放,便于GC回收
					expectedModCount++;
				}
				
				public void set(E e){
					//更新当前结点的信息
					if(lastReturned == null)
						throw new IllegalStateException();
					checkForComodification();
					lastReturned.item = e;//为什么可以直接修改?不需要抛出异常吗???
				}
				
				public void add(E e){
					//在本结点后面插入一个新结点
					checkForComodification();
					lastReturned = null;//首先将当前结点指针只向空
					if(next == null)//如果下一个结点为空,说明当前结点是最后一个,故插入链表尾就行
						linkLast(e);
					else
						linkBefore(e, next);//否则就将元素插入到当前结点后面
					nextIndex++;//将指针后移一位
					expectedModCount++;
				}
				
				public void forEachRemaining(Consumer<? super E> action){
					//遍历链表中的各个元素,使用lambda表达式
					Objects.requireNonNull(action);
					while(modCount == expectedModCount && nextIndex < size){
						action.accept(next.item);
						lastReturned = next;
						next = next.next;
						nextIndex++;
					}
					checkForComodification();
				}
				
				final void checkForComodification(){
					//同步检查
					if(modCount != expectedModCount)
						throw new ConcurrentModificationException();
				}
			}
			
			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 Iterator<E> descendingIterator(){
				//应该是获取一个从链表末尾开始遍历的迭代器
				return new DescendingIterator();
			}
			
			private class DescendingIterator implements Iterator<E>{
				//私有内部类,主要是用ListItr类实现从尾部向前迭代的功能
				private final ListItr itr = new ListItr(size());
				public boolean hasNext(){
					return itr.hasPrevious();
				}
				public E next(){
					return itr.previous();
				}
				public void remove(){
					itr.remove();
				}
			}
			
			@SuppressWarnings("unchecked")
			private LinkedList<E> superClone(){
				//私有方法,调用父类的克隆方法
				try{
					return(LinkedList<E>)super.clone();
				}catch(CloneNotSupportedException e){
					throw new InternalError(e);
				}
			}
			
			public Object clone(){
				//将链表克隆,返回一个克隆对象
				LinkedList<E> clone = superClone();
				clone.first = clone.last = null;
				clone.size = 0;
				clone.modCount = 0;
				for(Node<E> x = first; x != null; x = x.next)
					clone.add(x.item);
				return clone;
			}
			
			public Object[] toArray(){
				//将链表转变为数组,此处和ArrayList和vetor并不一样
				//这里是遍历链表,将链表的每一个结点的元素提取出来
				//放入一个新创建的数组中,最后返回这个数组
				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)
					a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(),size);
				int i = 0;
				Object[] result = a;
				for(Node<E> x = first; x != null; x = x.next)
					result[i++] = x.item;
				if(a.length > size)
					a[size] = null;
				return a;
			}
			
			private static final long serialVersionUID = 876323262645176354L;
			//验证版本是否一致,反序列化
			
			private void writeObject(java.io.ObjectOutputStream s)
				throws java.io.IOException{
				//序列化,写入
					s.defaultWriteObject();
					s.writeInt(size);
					for(Node<E> x = first; x != null; x = x.next)
						s.writeObject(x.item);
				}
			
			@SuppressWarnings("unchecked")
			private void readObject(java.io.ObjectInputStream s)
				throws java.io.IOException, ClassNotFoundException{
					//序列化,读取
					s.defaultReadObject();
					int size = s.readInt();
					for(int i = 0; i < size; i++)
						linkLast((E)s.readObject());
				}
				
			@Override
			public Spliterator<E> spliterator(){
				//获取分割器
				return new LLSpliterator<E>(this, -1, 0);
			}
			
			static final class LLSpliterator<E> implements Spliterator<E>{
				static final int BATCH_UNIT = 1 << 10;
				static final int MAX_BATCH = 1 << 25;
				final LinkedList<E> list;
				Node<E> current;
				int est;
				int expectedModCount;
				int batch;
				
				LLSpliterator(LinkedList<E> list, int est, int expectedModCount){
					//分割器构造器
					this.list = list;
					this.est = est;
					this.expectedModCount = expectedModCount;
				}
				
				final int getEst(){
					int s;
					final LinkedList<E> lst;
					if((s = est) < 0){
						if((lst = list) == null)
							s = est = 0;
						else{
							expectedModCount = lst.modCount;
							current = lst.first;
							s = est = lst.size;
						}
					}
					return s;
				}
				
				public long estimateSize(){
					return (long) getEst();
				}
				
				public Spliterator<E> trySplit(){
					Node<E> p;
					int s = getEst();
					if(s > 1 && (p = current) != null){
						int n = batch + BATCH_UNIT;
						if(n > s)
							n = s;
						if(n > MAX_BATCH)
							n = MAX_BATCH;
						Object[] a = new Object[n];
						int j = 0;
						do{
							a[j++] = p.item;
						}while((p = p.next) != null && j < n);
						current = p;
						batch = j;
						est = s - j;
						return Spliterators.spliterator(a, 0,j,Spliterator.ORDERED);
					}
					return null;
				}
				
				public void forEachRemaining(Consumer<? super E> action){
					Node<E> p;
					int n;
					if(action == null)
						throw new NullPointerException();
					if((n = getEst()) > 0 && (p = current) != null){
						current = null;
						est = 0;
						do{
							E e = p.item;
							p = p.next;
							action.accept(e);
						}while(p != null && --n > 0);
					}
					if(list.modCount != expectedModCount)
						throw new ConcurrentModificationException();
				}
				
				public boolean tryAdvance(Consumer<? super E> action){
					Node<E> p;
					if(action == null)
						throw new NullPointerException();
					if(getEst() > 0 && (p = current) != null){
						--est;
						E e = p.item;
						current = p.next;
						action.accept(e);
						if(list.modCount != expectedModCount)
							throw new ConcurrentModificationException();
						return true;
					}
					return false;
				}
				
				public int characteristics(){
					return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
				}
			}
		}	
		

再来分析一下LinkedList的常用方法:

1.size

public int size(){
//获得链表长度
    return size;
}

获取链表长度,不用多说。

2.contains与indexOf

public boolean contains(Object o){
//查询链表中是否含有某元素
	return indexOf(o) != -1;
}

public int indexOf(Object o){
//根据内容查找链表中第一个出现该元素的结点的索引
	int index = 0;
	if(o == null){
	    for(Node<E> x = first; x != null; x = x.next){
		if(x.item == null)
			return index;
		index++;
	    }
	}else{
	    for(Node<E> x = first; x != null; x = x.next){
		if(o.equals(x.item))
			return index;
		index++;
	    }
	}
	return -1;
}
			
public int lastIndexOf(Object o){
//根据元素逆序查找链表中最后一次出现该元素的结点的索引
	int index = size;
	if(o == null){
		for(Node<E> x = last; x != null; x = x.prev){
			index--;
			if(x.item == null)
				return index;
		}
	}else{
		for(Node<E> x = last; x != null; x = x.prev){
			index--;
			if(o.equals(x.item))
				return index;
		}
	}
	return -1;
}

此处contains和indexOf的实现方式和ArrayList、Vector没有什么太大的差别,只是将Integer的遍历变成了Node遍历。

3.查询

public E get(int index){
//获得索引中的结点内的内容
    checkElementIndex(index);
    //核查索引是否合法
    return node(index).item;
    //此处使用的是node的方法,即先判断索引的大小
    //如果比链表的长度的一半小,则从前遍历寻找
    //否则从链表的末尾开始向前寻找,提高的效率
}

Node<E> node(int 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;
    }
}


public E getFirst(){
//获得链表头结点的内含元素
	final Node<E> f = first;
	if(f == null)
		throw new NoSuchElementException();
	return f.item;
}

public E getLast(){
//获取链表尾结点的内含元素
	final Node<E> l = last;
	if(l == null)
		throw new NoSuchElementException();
	return l.item;
}

public E peek(){
//类似于栈的取栈顶元素,此处也是取链表头部结点中的元素
//但是不会移除头结点,且当头结点为空时返回空,不会抛出异常
//此处实现栈中的方法
	final Node<E> f = first;
	return (f == null)
		? null
		: f.item;
}

public E element(){
//也是取头结点元素,但是如果头结点为空时会抛出异常
	return getFirst();
}

public E peekFirst(){
//此处实现双向队列中的方法
	final Node<E> f = first;
	return (f == null)
		? null
		: f.item;
}

public E peekLast(){
//双向队列中取出尾部元素,不删除
	final Node<E> l = last;
	return (l == null)
		? null
		: l.item;
}

从上面源代码可以看出,LinkedList在查找某个元素这个功能上提供了很多的方法,而且有很多方法感觉都是一样的,那这样算不算一种代码冗余呢?嗯,首先来给这些方法分一下类,我们知道LinkedList既实现了List接口又实现Deque接口,所以两个体系中的方法都需要实现。那么get和element两个方法其实是属于List体系的方法,而getFirst与getLast和peekFirst与peekLast这四个方法是Deque中的双向队列的方法,而getFirst和getLast方法在面对头部结点或尾部结点为空时会抛出异常,而peekFirst和peekLast这两个方法面对头部或尾部为空时会返回null。而另外一个peek方法则是Deque中的栈的方法。

简单来说,即:

Listget(index)根据索引获取索引处的元素内容
ListElement()获取表头的内容
Deque(双向队列)peekFirst()获取队列头部的内容,如果队列头部为空,返回null
Deque(双向队列)peekLast()获取队列尾部的内容,如果队列尾部为空,返回null
Deque(双向队列)getFirst()获取队列头部的内容,如果队列头部为空,抛出无此元素异常
Deque(双向队列)getLast()获取队列尾部的内容,如果队列尾部为空,抛出无此元素异常
Deque(栈)peek()获取栈顶内容,如果栈为空,返回null

4.增加

public boolean add(E e){
//在链表尾部插入结点,如果成功返回true
	linkLast(e);
	return true;
}

public void add(int index, E element){
//在某个索引所指的结点前面添加一个结点
	checkPositionIndex();
//核查索引是否合法,此处方法和checkElementIndex方法
//的不同之处在于此处索引可以是链表的长度而c方法不可以
	if(index == size)
		linkLast(element);
	else
		linkBefore(element,node(index));
}

public void addFirst(E e){
//在链表头部插入结点,对外方法
	linkFirst(e);
}

public void addLast(E e){
//在链表尾部插入结点,对外方法
	linkLast(e);
}

public boolean offer(E e){
//offer是队列中的方法,即快速插入元素,此处调用add进行插入
//此处实现双向队列中的方法
	return add(e);
}

public boolean offerFirst(E e){
//将元素插入到链表头部
	addFirst(e);
	return true;
}

public boolean offerLast(E e){
//将元素插入到链表尾部
	addLast(e);
	return true;
}

public void push(E e){
//实现栈中压栈操作
	addFirst(e);
}


增加方法也类似,其中add方法属于List体系中的,但是Deque接口中也有相同的方法,两者都需要实现。offerFirst、offerLast、addFirst、addLast都是属于Deque体系中的双向队列,而offer属于Deque体系中的队列,push属于Deque体系中的栈。

Listadd(e)在链表尾部插入一个元素
Listadd(index,element)在链表index索引处插入一个元素
Deque(双向队列)addFirst(e)在双向队列头部插入一个元素,插入失败时抛出异常
Deque(双向队列)addLast(e)在双向队列尾部插入一个元素,插入失败时抛出异常
Deque(双向队列)offerFirst(e)在双向队列头部插入一个元素,插入失败时返回false
Deque(双向队列)offerLast(e)在双向队列尾部插入一个元素,插入失败时返回false
Deque(队列)offer(e)在队列尾部插入一个元素
Deque(栈)push(e)将元素压入栈

5.修改

public E set(int index, E element){
//修改某个索引指向的结点的信息
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

由于Deque接口没有提供与修改相关的方法,因此LinkedList只实现了List接口中的修改方法,首先检查传入的索引的合法性,然后再遍历到索引处,将索引指向的原元素取出,并修改成新元素,然后返回原元素。由此可见ArrayList的修改比较麻烦,必须从链表头部一个个遍历下去,找到元素之后才能进行修改。

6.删除

public boolean remove(Object o){
//移除某一个结点,该结点的内容是o
//此处应用for循环进行链表遍历,强,从没想过
	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;
}

public E remove(int index){
//移除索引所指向的链表结点
	checkElementIndex(index);
	return unlink(node(index));
}

public boolean removeFirstOccurrence(Object o){
//删除第一次出现的某元素,如果该元素不存在,则不做任何动作
	return remove(o);
}

public boolean removeLastOccurrence(Object o){
//删除最后一次出现的某元素,如果该元素不存在,则不做任何动作
	if(o == null){
		for(Node<E> x = last; x != null; x = x.prev){
			if(x.item == null){
			unlink(x);
			return true;
			}
		}
	}else{
		for(Node<E> x = last; x != null; x = x.prev){
			if(o.equals(x.item)){
			unlink(x);
			return true;
			}
		}
	}
	return false;
}

remove方法既是List接口的也是Deque接口的,所以一起实现,且在LinkedList中也重载了一次,当参数是索引时,那就从头部遍历到该索引处并删除该结点,当参数是元素时,则从头部遍历找到该元素第一次出现时的结点,将该结点删除。除此之外,Deque接口还有另外两个删除方法,即removeFirstOccurence和removeLastOccurrence,功能分别是删除正序出现的第一个元素和逆序出现的第一个元素,当没找到该元素时,返回false.

7.弹出

public E removeFirst(){
//移除链表第一个结点,调用的unlinkFirst方法,
//主要是检查了该结点的安全性,即验证结点是否为空
	final Node<E> f = first;
	    if(f == null)
		throw new NoSuchElementException();
	return unlinkFirst(f);
}

public E removeLast(){
//和移除头结点类似,移除链表的尾结点
	final Node<E> l = last;
		if(l == null)
			throw new NoSuchElementException();
	return unlinkLast(l);
}

public E pollFirst(){
//双向队列中取出头部元素并删除头部结点
	final Node<E> f = first;
		return (f == null)
			? null
			: unlinkFirst(f);
}


public E pollLast(){
//双向队列中取出尾部元素并删除尾部结点
	final Node<E> l = last;
		return (l == null)
			? null
			: unlinkLast(l);
}

public E pop(){
//实现栈中弹栈操作
	return removeFirst();
}


所谓弹出,也就是将元素从该容器中删除并且取出来,这个方法List体系中是没有的,但是Deque体系中有,如pollFirst、pollLast、removeFirst、removeLast是属于Deque中的双向队列的;而pop既是Deque中的栈的也是其中队列的。

Deque(双向队列)removeFirst()弹出双向队列中头部元素,如果头部元素不存在,则抛出无此元素异常
Deque(双向队列)removeLast()弹出双向队列中尾部元素,如果尾部元素不存在,则抛出无此元素异常
Deque(双向队列)pollFirst()弹出双向队列中头部元素,如果头部元素不存在,则返回null
Deque(双向队列)pollLast()弹出双向队列中尾部元素,如果尾部元素不存在,则返回null
Deque(队列)poll()弹出队列中的头部元素,如果头部元素不存在,抛出无此元素异常
Deque(栈)poll()弹出栈中的头部元素,如果头部元素不存在,抛出无此元素异常

8.其他常用方法

public void clear(){
//清空链表中的所有结点,即将所有的结点都释放
	for(Node<E> x = first; x != null; ){
		Node<E> next = x.next;
		x.item = null;
		x.next = null;
		x.prev = null;
		x = next;
	}
	first = last = null;
	size = 0;
	modCount++;
}

public Object[] toArray(){
//将链表转变为数组,此处和ArrayList和vetor并不一样
//这里是遍历链表,将链表的每一个结点的元素提取出来
//放入一个新创建的数组中,最后返回这个数组
	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)
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(),size);
	int i = 0;
	Object[] result = a;
	for(Node<E> x = first; x != null; x = x.next)
		result[i++] = x.item;
		if(a.length > size)
			a[size] = null;
	return a;
}

其他的一些常用方法也和ArrayList差不多,此处就不再赘述,另外需要提到的一点是,为什么LinkedList源代码中没有isEmpty方法呢?难道LinkedList不能判断是否为空么?

其实它的祖类(父类的父类的父类,应该可以这么说吧:)AbstractCollection已经有这个方法的实现了,只是ArrayList和Vector都重写了一遍而已,而LinkedList并没有再重写,所以就继承下来了,也可以用的。

 

好了,说了这么多,最后总结一下吧,LinkedList与ArrayList完全不一样,它的直接父类是AbstractSequentialList,而且还额外实现了一个接口Deque,这些不同使得它具备的功能远比ArrayList多,再加上其底层是使用结点一个一个指向连接所形成的,所以它可以当作链表来使用,也可以当作队列、栈和双向队列来使用。由于是链表结构,自然也就没有扩容之类的说法,并且由于这种结构,使得它具有增删快,查改慢的特点。总之,LinkedList的方法很多,而且类似的方法也很多,因此需要弄清楚每一个方法都是怎么实现的,都可以应用在哪种情况下是很有必要的。

 

参考博客:

https://www.cnblogs.com/xujian2014/p/4630785.html

https://www.cnblogs.com/bushi/p/6681543.html

https://blog.youkuaiyun.com/u011240877/article/details/52834074

https://www.cnblogs.com/skywang12345/p/3308807.html

https://zhidao.baidu.com/question/459001299120564565.html

https://www.cnblogs.com/chenssy/p/3514524.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值