SpringMVC+Mybatis框架整合源码

LinkedList简介

  LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈、队列和双端队列来使用。

  LinkedList同样是非线程安全的,只在单线程下适合使用。

  LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆。

LinkedList源码

  以下是linkedList源码(加入简单代码注释):

LinkedList详细分析

一、什么是链表

  链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链表又分为单向链表和双向链表,而单向/双向链表又可以分为循环链表和非循环链表,下面简单就这四种链表进行图解说明。

  1.单向链表

    单向链表就是通过每个结点的指针指向下一个结点从而链接起来的结构,最后一个节点的next指向null。

 

    

 

  2.单向循环链表

    单向循环链表和单向列表的不同是,最后一个节点的next不是指向null,而是指向head节点,形成一个“环”。

    

 

  3.双向链表

     从名字就可以看出,双向链表是包含两个指针的,pre指向前一个节点,next指向后一个节点,但是第一个节点head的pre指向null,最后一个节点的tail指向null。

    

  4.双向循环链表

    双向循环链表和双向链表的不同在于,第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,也形成一个“环”。而LinkedList就是基于双向循环链表设计的。

    

 

二、定义

  看一下LinkedList的定义部分:

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

  可以看出LinkedList 继承AbstractSequentialList 抽象类,实现了List,Deque,Cloneable,Serializable 几个接口,AbstractSequentialList 继承 AbstractList,是对其中方法的再抽象,其主要作用是最大限度地减少了实现受“连续访问”数据存储(如链接列表)支持的此接口所需的工作,简单说就是,如果需要快速的添加删除数据等,用AbstractSequentialList抽象类,若是需要快速随机的访问数据等用AbstractList抽象类。

  Deque 是一个双向队列,也就是既可以先入先出,又可以先入后出,再直白一点就是既可以在头部添加元素又在尾部添加元素,既可以在头部获取元素又可以在尾部获取元素。看下Deque的源码(已删除注释):

三、内部类

  LinkedList总共有三个内部类,ListItr,Entry<E>和DescendingIterator,这三个内部类分别实现了List迭代器,双向链表的节点所对应的数据结构和反向迭代器。

  3.1内部类ListItr

  源码在上面的代码中已经给出,这里不再给出,分析一下

  3.1.1构造函数

 

  在这里,这个构造函数只做了一件事情,就是加速查找,重点是这句话if (index < 0 || index > size)如果给出的index索引小于双向链表长度的一半,则从头向后查找,如果大于,则从尾向前查找。

  3.1.2成员变量

  这个内部类当中的其他方法这里就不介绍了,set(),get(),add()等等这些方法,在最开始的注释代码中写的很清晰了,这里说其中的一个成员变量private int expectedModCount = modCount;

  首先来看一下这个modCount是个什么东西:它是LinkedList继承的父类的父类AbstractList<E>中的一个成员变量protected transient int modCount = 0;它是用来记录AbstractList结构性变化的次数。http://item.taobao.com/item.htm?id=41222768202

  在AbstractList的所有涉及结构变化的方法中都增加modCount的值,包括:add()、remove()、addAll()、removeRange()及clear()方法。这些方法每调用一次,modCount的值就加1。

  而在这里ListItr自己同时维护了一个expectedModCount,初始值是取的modCount,而当ListItr结构性变化的时候expectedModCount也会自动修改,包括next(),previous(),remove(),add(E e)等方法,而同样在这些方法里面的第一步都会去调用checkForComodification()这个方法进行修改的同步检查,如果不同步将会抛出ConcurrentModificationException()这个异常。

  3.2内部类Entry<E>

  3.2.1构造方法

 

 

  这个构造函数使用到了三个参数,分别是当前节点值,下一节点和上一节点。

  3.2.2addBefore(E e, Entry<E> entry)

  在这个内部类中,实现了将将节点e添加到entry节点之前的这个方法,这个方法在LinkedList所有添加操作相关的方法中都调用了,源码:

  在这个方法一开始的地方就new了一个新的Entry对象出来,经典的双向列表固定位置添加新节点,并且更新前驱结点的next域和后驱结点的previous域,并且将记录操作数的modCount自增1。http://item.taobao.com/item.htm?id=41222768202

  3.3内部类DescendingIterator

  反向迭代器实现类,这个类比较简单,直接调用的ListItr的方法,在此不再赘述。

四、增加

  第一个add在尾部增加元素比较好理解:在header前添加元素e,header前就是最后一个结点,就是在最后一个结点的后面添加元素e;而第二个增加就不是那么简单了,同样都是调用Entry的addBefore方法,第二个方法增加了一句判断条件index==size ? header : entry(index),这个条件实际上的意思就是如果index等于list元素个数,则在队尾添加元素(header之前),否则在index节点前添加元素。

  到这里可以发现一点,header作为双向循环链表的头结点是不保存数据的,也就是说hedaer中的element永远等于null

  第一个addAll方法是调用了第二个,仔细研究一下第二个addAll的方法,首先第一句话是越界检查,第二局判断是对添加的Collection做非0校验,Entry<E> successor = (index==size ? header : entry(index));这句话的含义是:获取要插入index位置的下一个节点,如果index正好是lsit尾部的位置那么下一个节点就是header,否则需要查找index位置的节点,而Entry<E> predecessor = successor.previous;这句话的含义是:获取要插入index位置的上一个节点,因为是插入,所以上一个点击就是未插入前下一个节点的上一个。

五、删除、修改、查询

  5.1删除

  在这里删除本质上的意思就是前一节点和后一节点组合在一起就好了,互相修改一下前驱结点和后置节点

  5.2修改

  set方法看起来简单了很多,只要修改该节点上的元素就好了。

  5.3查找

  LinkedList是通过从header开始index计为0,然后一直往下遍历(next),直到到底index位置。为了优化查询效率,LinkedList采用了二分查找(这里说的二分只是简单的一次二分),判断index与size中间位置的距离,采取从header向后还是向前查找。
  基于双向循环链表实现的LinkedList,通过索引Index的操作时低效的,index所对应的元素越靠近中间所费时间越长。而向链表两端插入和删除元素则是非常高效的(如果不是两端的话,都需要对链表进行遍历查找)。

六、是否包含

  indexOf查询元素位于容器的索引位置,都是需要对链表进行遍历操作,也都是低效的操作,慎用。

七、LinkedList实现的双端队列

  很简单,逻辑都是基于上面讲的链表操作的。

八、总结

  8.1从源码中很明显可以看出,LinkedList的实现是基于双向循环链表的,且头结点中不存放数据

  8.2注意两个不同的构造方法。无参构造方法直接建立一个仅包含head节点的空链表,包含Collection的构造方法,先调用无参构造方法建立一个空链表,而后将Collection中的数据加入到链表的尾部后面。

  8.3在查找和删除某元素时,源码中都划分为该元素为null和不为null两种情况来处理,LinkedList中允许元素为null。

  8.4LinkedList是基于链表实现的,因此不存在容量不足的问题,所以这里没有扩容的方法。

  8.5注意源码中的Entry<E> entry(int index)方法。该方法返回双向链表中指定位置处的节点,而链表中是没有下标索引的,要指定位置出的元素,就要遍历该链表,从源码的实现中,我们看到这里有一个加速动作。源码中先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处。这样可以减少一部分不必要的遍历,从而提高一定的效率(实际上效率还是很低)。

  8.6注意链表类对应的数据结构Entry。

  8.7LinkedList是基于链表实现的,因此插入删除效率高,查找效率低(虽然有一个加速动作)。

  8.8要注意源码中还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值