源码分析——LinkedList

继续分析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--;//链表长度减一
				modCoun
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值