博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
源码分析之 LinkedList
阅读量:6399 次
发布时间:2019-06-23

本文共 5209 字,大约阅读时间需要 17 分钟。

概念

LinkedListJava Collections FrameworkList接口一种实现。不同于ArrayList的是LinkedList是基于双向链表实现的。

类结构

LinkedList类结构

LinkedList继承AbstractSequentialList类,实现List<E>,Deque<E>, Cloneable, java.io.Serializable接口。

AbstractSequentialList

AbstractSequentialList类是AbstractList子类,同时也提供了一个基本的list接口的实现,为顺序访问的数据存储结构(链表)提供了最小化的实现。而对于随机访问的数据存储结构(数组)要优先考虑使用AbstractListAbstractSequentiaList是在迭代器基础上实现的getsetadd等方法。

Deque / Queue

Deque接口继承Queue接口,两端都允许插入和删除元素,即双向队列。LinkedList实现了Deque接口。这也就意味着我们可以利用LinkedList来实现双向队列。

类成员

构造函数

public LinkedList() {    }    public LinkedList(Collection
c) { this(); addAll(c); }复制代码

LinkedList类提供了2个构造函数,其中有一个带有Collection参数的构造函数。

size

transient int size = 0;复制代码

表示链表的大小。

first / last

transient Node
first; transient Node
last;复制代码

firstlast均是Node类的实例。first指向头结点,last指向的尾节点。

Node类

Node类是LinkedList的私有内部类,也是链表数据存储的基本单元。

private static class Node
{ E item; Node
next; Node
prev; Node(Node
prev, E element, Node
next) { this.item = element; this.next = next; this.prev = prev; } }复制代码

Node类有3个成员变量:

  • item:代表数据元素本身;
  • next:指向数据的后节点;
  • prev:指向数据的前节点;

由此可以看出,LinkedList存储的数据构建于一个双向链表中。

add(E) 方法

public boolean add(E e) {        linkLast(e);        return true;    }    void linkLast(E e) {        // 现在的尾节点        final Node
l = last; // 包装元素数据构建新节点 final Node
newNode = new Node<>(l, e, null); last = newNode; if (l == null) // 如果尾节点为空,将新节点赋值给first节点 first = newNode; else // 如果为节点不为空,将新节点添加至尾部 l.next = newNode; size++; modCount++; }复制代码

add方法会调用linkLast方法,将添加的元素加入链表尾部。

add(int index, E element) 方法

public void add(int index, E element) {        // 检查index是否有效        checkPositionIndex(index);        if (index == size)           // 如果index正好等于size,说明此时index位置正好在链表尾部,则直接在尾部添加            linkLast(element);        else            linkBefore(element, node(index));    }复制代码

大部分的情况,添加元素的index都不等于size。这时候会调用linkBefore(element, node(index));来进行添加元素。首先会调用node(index)获取在指定index处的node节点:

Node
node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { // index 小于size一半的情况下,从fist节点开始遍历 Node
x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { // index大于size一半的情况下,从last节点开始遍历 Node
x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }复制代码

最后调用linkBefore添加元素:

void linkBefore(E e, Node
succ) { // assert succ != null; // succ节点前节点 final Node
pred = succ.prev; // 构建新节点,前节点为pred,后节点为succ final Node
newNode = new Node<>(pred, e, succ); // 在succ和pred之间插入新节点 succ.prev = newNode; if (pred == null) // 如果pred为null,将新节点复制为first节点 first = newNode; else // 否则添加为pred的后节点 pred.next = newNode; size++; modCount++; }复制代码

remove(index) 方法

根据index删除链表某节点:

public E remove(int index) {        // 检查index是否处于正确范围        checkElementIndex(index);        return unlink(node(index));    }     E unlink(Node
x) { // assert x != null; final E element = x.item; final Node
next = x.next; final Node
prev = x.prev; if (prev == null) { //x的前节点为null,将x的后节点设置为fist节点 first = next; } else { // 将x前节点的后节点指向为x的后节点 prev.next = next; x.prev = null; } if (next == null) { // x的后节点为null,将x的前节点设置为last节点 last = prev; } else { // 将x后节点的前节点指向为x的前节点 next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }复制代码
  • 检查index是否在合理范围;
  • 调用node(index)找出index位置的节点;
  • 调用unlink方法删除节点;

remove(o) 方法

根据元素本身删除链表某节点:

public boolean remove(Object o) {        if (o == null) {        // 如果o为null,遍历删除链表中为null的节点            for (Node
x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node
x = first; x != null; x = x.next) { // 遍历链表找出等于o的节点并删除 if (o.equals(x.item)) { unlink(x); return true; } } } return false; }复制代码

其他操作元素的方法

除了以上所描述的几种操作LinkedList链表元素的方法,LinkedList类还提供了很多操作元素的方法。有实现Deque接口的addFirstaddLastofferofferFirst...等等

ArrayList VS LinkedList

  • ArrayList是基于动态数组实现的,LinkedList是基于双向链表实现的;
  • 对于随机访问来说,ArrayList要优于LinkedList
    • ArrayList通过数组下标;
    • LinkedList需要遍历寻址;
  • 不考虑直接在尾部添加数据的话,ArrayList按照指定的index添加/删除数据是通过复制数组实现。LinkedList通过寻址改变节点指向实现。
  • LinkedList在数据存储上不存在浪费空间的情况。ArrayList动态扩容会导致有一部分空间是浪费的。

转载地址:http://zlyea.baihongyu.com/

你可能感兴趣的文章
Docker背后的内核知识——cgroups资源限制(转)
查看>>
Java技术——Java泛型详解(转)
查看>>
某个第三方支付平台数据库的分析、学习与总结(转)
查看>>
Linux Linux程序练习八
查看>>
读书笔记:《写给大家看的设计书》
查看>>
Predicate与filter
查看>>
UITableViewCell 取消分隔线
查看>>
性能测试工具 nGrinder 项目剖析及二次开发
查看>>
Mybatis集成pagehelper and jsqlparser(分页)
查看>>
50+ 顶级开源 Kubernetes 工具列表
查看>>
老年夫妇为何分床睡?83岁的奶奶是这样说的……
查看>>
恶意软件日均进攻百万次!三大方法保护Hadoop集群免遭攻击!
查看>>
小米品牌年度盛典今日开启 多款明星产品引爆抢购热潮
查看>>
猎豹智库Q3最新数据:ofo用户活跃渗透率连续领先摩拜 稳居第一
查看>>
手把手教你HDFS基础配置安装及命令使用!
查看>>
机器人索菲亚打算生儿育女,连孩子名字都想好了!
查看>>
中国最西北高寒铁路线铺就春运“平安路”
查看>>
春运将至 民航提前迎来客流高峰
查看>>
安徽:0.1元优粮优购的正效应
查看>>
Google发布机器学习开源可视化工具Facets
查看>>