前言

上一篇文章讲了ArrayList和Vector,这两者是基于数组,还是比较好理解的,LinkedList是基于链表实现的,所以适合有数据结构基础的同学阅读~ 本文所有代码都是基于JDK8的

LinkedList

List源码 扩展JAVA list源码解析_数组


从结构上,我们还看到了LinkedList实现了Deque接口,因此,我们可以操作LinkedList像操作队列和栈一样,LinkedList的底层是链表,先来看一下节点的定义:

List源码 扩展JAVA list源码解析_数组_02


从节点的定义可以看出LinkedList是双向链表,学过数据结构的同学应该对链表的结构还是比较熟悉的,这里就不展开说了。LinkedList的属性只有三个:头尾节点、以及size:

List源码 扩展JAVA list源码解析_List源码 扩展JAVA_03

构造方法

LinkedList有两个构造方法,如下图:

List源码 扩展JAVA list源码解析_JDK源码_04


分别是空构造方法,和一个根据其他容器添加元素的构造方法

Link相关方法

Link相关方法都是private或者default的,也就是说在外部并不能访问到。之所以将Link相关方法写进来是因为Link相关方法对我们学习链表还是非常有帮助的,并且removeLast、removeFirst、addLast、addFirst、add等方法其实都是调用了Link相关方法。Link相关方法分别有:

  • void linkFirst(E e) :在头节点前插入一个元素
  • void linkLast(E e) :在尾节点后插入一个元素
  • void linkBefore(E e, Node succ) :在指定节点前插入一个元素
  • E unlinkFirst(Node f) :删除头节点
  • E unlinkLast(Node l) : 删除尾节点
  • E unlink(Node x) :删除指定节点

这里方法比较多,我们只挑几个看看:

  • linkFirst方法:
  • List源码 扩展JAVA list源码解析_构造方法_05

  • 实现步骤:
    1、创建新节点,新节点的last指向头节点
    2、头结点指向新建节点
    3、判断原链表是否为空,如果为空将尾节点指向头结点,如果不为空将旧头节点指向新头节点
  • linkBefore方法:
  • List源码 扩展JAVA list源码解析_构造方法_06

  • 在指定节点前插入节点的实现步骤也类似,多了一步前节点prev的指向
  • unlink方法
  • List源码 扩展JAVA list源码解析_JDK源码_07

  • 删除一个节点分为好几种情况:
  • 前节点为空,后节点不为空:此时为头节点,将头节点指向后节点,然后将后节点的前节点置空。
  • 前节点不为空,后节点为空:此时为尾节点,将前节点指向后节点(null),同时成为尾节点
  • 前节点不为空,后节点不为空:将前后节点分别指向对方

Link相关方法真的写的非常的简洁!非常值得我们学习

add方法

List源码 扩展JAVA list源码解析_List源码 扩展JAVA_08


add方法其实也就是调用了linkLast方法,看下LinkLast方法:

List源码 扩展JAVA list源码解析_数组_09


和linkFirst方法非常的像啊,有木有~

remove方法

  • 先看一下remove(Object o)方法,从链表中删除指定元素
  • List源码 扩展JAVA list源码解析_构造方法_10

  • remove方法的思想也很简单,先判断o是否为空,因为如果o为空则不能使用equals来比较。然后对链表进行遍历。注意,这里比较的是两个对象的地址!使用的是object的equals方法。
  • 再看一下remove(int index)方法,从链表中删除指定下标元素
  • List源码 扩展JAVA list源码解析_链表_11

  • checkElementIndex方法是检查index是否越界,这里的node(int index)方法比较重要,这个方法的作用是从链表中找到指定下标的节点,来看一下:
  • List源码 扩展JAVA list源码解析_链表_12

if (index < (size >> 1))

这句话是用于判断所处节点是靠近头节点还是尾节点,这样搜索的效率更高一些,然后就是进行for循环直到指定下标,返回指定下标的节点。

push、pop方法

List源码 扩展JAVA list源码解析_数组_13


push、pop方法分别调用了addFirst和removeFirst方法,addFirst方法中调用了linkFirst方法,removeFirst方法中调用了unlinkFirst方法。所以这也是我为什么一开始就先将link相关方法的原因,因为link方法虽然在外部不能直接使用,但是有关链表的删除、插入操作都是通过调用link相关方法来实现的。对link相关方法还不了解的同学可以翻到最前面复习一下~

set方法

List源码 扩展JAVA list源码解析_List源码 扩展JAVA_14


首先检查下标是否越界,然后调用node(index)方法找到对应下标的节点,然后设置新值,返回旧值。

总结

关于三大集合的源码到这里就讲完了,代码感觉也不是很难,只要仔细阅读一定可以看懂,下面做一个总结:

- ArrayList:

底层实现是数组
ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍
在增删时候,需要数组的拷贝复制(navite 方法由C/C++实现)

- LinkedList:

底层实现是双向链表[双向链表方便实现往前遍历]

- Vector:

底层是数组,现在已少用,被ArrayList替代,原因有两个:

Vector所有方法都是同步,有性能损失。
Vector初始length是10 超过length时 以100%比率增长,相比于ArrayList更多消耗内存。

因此查询多的时候用ArrayList,增删多用LinkedList