首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >说一下 ArrayList 和 LinkedList 的区别?

说一下 ArrayList 和 LinkedList 的区别?

作者头像
用户9995743
发布于 2022-12-22 08:39:16
发布于 2022-12-22 08:39:16
40100
代码可运行
举报
文章被收录于专栏:彭旭锐彭旭锐
运行总次数:0
代码可运行

大家好,我是小彭。

在上一篇文章里,我们聊到了基于动态数组 ArrayList 线性表,今天我们来讨论一个基于链表的线性表 —— LinkedList。


小彭的 Android 交流群 02 群已经建立啦,扫描文末二维码进入~


思维导图:


1. LinkedList 的特点

1.1 说一下 ArrayList 和 LinkedList 的区别?

  • 1、数据结构 在数据结构上,ArrayList 和 LinkedList 都是 “线性表”,都继承于 JavaList 接口。另外 LinkedList 还实现了 Java 的 Deque 接口,是基于链表的栈或队列,与之对应的是 ArrayDeque 基于数组的栈或队列;
  • 2、线程安全: ArrayList 和 LinkedList 都不考虑线程同步,不保证线程安全;
  • 3、底层实现: 在底层实现上,ArrayList 是基于动态数组的,而 LinkedList 是基于双向链表的。事实上,它们很多特性的区别都是因为底层实现不同引起的。比如说:
    • 在遍历速度上: 数组是一块连续内存空间,基于局部性原理能够更好地命中 CPU 缓存行,而链表是离散的内存空间对缓存行不友好;
    • 在访问速度上: 数组是一块连续内存空间,支持 O(1) 时间复杂度随机访问,而链表需要 O(n) 时间复杂度查找元素;
    • 在添加和删除操作上: 如果是在数组的末尾操作只需要 O(1) 时间复杂度,但在数组中间操作需要搬运元素,所以需要 O(n)时间复杂度,而链表的删除操作本身只是修改引用指向,只需要 O(1) 时间复杂度(如果考虑查询被删除节点的时间,复杂度分析上依然是 O(n),在工程分析上还是比数组快);
    • 额外内存消耗上: ArrayList 在数组的尾部增加了闲置位置,而 LinkedList 在节点上增加了前驱和后继指针。

1.2 LinkedList 的多面人生

在数据结构上,LinkedList 不仅实现了与 ArrayList 相同的 List 接口,还实现了 Deque 接口(继承于 Queue 接口)。

Deque 接口表示一个双端队列(Double Ended Queue),允许在队列的首尾两端操作,所以既能实现队列行为,也能实现栈行为。

Queue 接口:

拒绝策略

抛异常

返回特殊值

入队(队尾)

add(e)

offer(e)

出队(队头)

remove()

poll()

观察(队头)

element()

peek()

Queue 的 API 可以分为 2 类,区别在于方法的拒绝策略上:

  • 抛异常:
    • 向空队列取数据,会抛出 NoSuchElementException 异常;
    • 向容量满的队列加数据,会抛出 IllegalStateException 异常。
  • 返回特殊值:
    • 向空队列取数据,会返回 null;
    • 向容量满的队列加数据,会返回 false。

Deque 接口:

Java 没有提供标准的栈接口(很好奇为什么不提供),而是放在 Deque 接口中:

拒绝策略

抛异常

等价于

入栈

push(e)

addFirst(e)

出栈

pop()

removeFirst()

观察(栈顶)

peek()

peekFirst()

除了标准的队列和栈行为,Deque 接口还提供了 12 个在两端操作的方法:

拒绝策略

抛异常

返回值

增加

addFirst(e)/ addLast(e)

offerFirst(e)/ offerLast(e)

删除

removeFirst()/ removeLast()

pollFirst()/ pollLast()

观察

getFirst()/ getLast()

peekFirst()/ peekLast()


2. LinkedList 源码分析

这一节,我们来分析 LinkedList 中主要流程的源码。

2.1 LinkedList 的属性

  • LinkedList 底层是一个 Node 双向链表,Node 节点中会持有数据 E 以及 prev 与next 两个指针;
  • LinkedList 用 firstlast 指针指向链表的头尾指针。

LinkedList 的属性很好理解的,不出意外的话又有小朋友出来举手提问了:

  • 🙋🏻‍♀️ 疑问 1:为什么字段都不声明 private 关键字?

这个问题直接回答吧。我的理解是:因为内部类在编译后会生成独立的 Class 文件,如果外部类的字段是 private 类型,那么编译器就需要通过方法调用,而 non-private 字段就可以直接访问字段。

  • 🙋🏻‍♀️ 疑问 2:为什么字段都声明 transient 关键字?

这个问题我们在分析源码的过程中回答。

疑问比 ArrayList 少很多,LinkedList 真香(还是别高兴得太早吧)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable {

    // 疑问 1:为什么字段都不声明 private 关键字?
    // 疑问 2:为什么字段都声明 transient 关键字?
    // 元素个数
    transient int size = 0;

    // 头指针
    transient Node<E> first;

    // 尾指针
    transient Node<E> last;

    // 链表节点
    private static class Node<E> {
        // 节点数据
        // (类型擦除后:Object item;)
        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;
        }
    }
}

2.2 LinkedList 的构造方法

LinkedList 有 2 个构造方法:

  • 1、无参构造方法: no-op;
  • 2、带集合的构造: 在链表末尾添加整个集合,内部调用了 addAll 方法将整个集合添加到数组的末尾。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 无参构造方法
public LinkedList() {
}

// 带集合的构造方法
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

// 在链表尾部添加集合
public boolean addAll(Collection<? extends E> c) {
    // 索引为 size,等于在链表尾部添加
    return addAll(size, c);
}

2.3 LinkedList 的添加方法

LinkedList 提供了非常多的 addXXX 方法,内部都是调用一系列 linkFirstlinkLastlinkBefore 完成的。如果在链表中间添加节点时,会用到 node(index) 方法查询指定位置的节点。

其实,我们会发现所有添加的逻辑都可以用 6 个步骤概括:

  • 步骤 1: 找到插入位置的后继节点(在头部插入就是 first,在尾部插入就是 null);
  • 步骤 2: 构造新节点;
  • 步骤 3: 将新节点的 prev 指针指向前驱节点(在头部插入就是 null,在尾部插入就是 last);
  • 步骤 4: 将新节点的 next 指针指向后继节点(在头部插入就是 first,在尾部插入就是 null);
  • 步骤 5: 将前驱节点的 next 指针指向新节点(在头部插入没有这个步骤);
  • 步骤 6: 将后继节点的 prev 指针指向新节点(在尾部插入没有这个步骤)。

分析一下添加方法的时间复杂度,区分在链表两端或中间添加元素的情况共:

  • 如果是在链表首尾两端添加: 只需要 O(1) 时间复杂度;
  • 如果在链表中间添加: 由于需要定位到添加位置的前驱和后继节点,所以需要 O(n) 时间复杂度。如果事先已经获得了添加位置的节点,就只需要 O(1) 时间复杂度。

添加方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void addFirst(E e) {
    linkFirst(e);
}

public void addLast(E e) {
    linkLast(e);
}

public boolean add(E e) {
    linkLast(e);
    return true;
}

public void add(int index, E element) {
    checkPositionIndex(index);
    if (index == size)
        // 在尾部添加
        linkLast(element);
    else
        // 在指定位置添加
        linkBefore(element, node(index));
}

public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}

// 在链表头部添加
private void linkFirst(E e) {
    // 1. 找到插入位置的后继节点(first)
    final Node<E> f = first;
    // 2. 构造新节点
    // 3. 将新节点的 prev 指针指向前驱节点(null)
    // 4. 将新节点的 next 指针指向后继节点(f)
    // 5. 将前驱节点的 next 指针指向新节点(前驱节点是 null,所以没有这个步骤)
    final Node<E> newNode = new Node<>(null, e, f);
    // 修改 first 指针
    first = newNode;
    if (f == null)
        // f 为 null 说明首个添加的元素,需要修改 last 指针
        last = newNode;
    else
        // 6. 将后继节点的 prev 指针指向新节点
        f.prev = newNode;
    size++;
    modCount++;
}

// 在链表尾部添加
void linkLast(E e) {
    final Node<E> l = last;
    // 1. 找到插入位置的后继节点(null)
    // 2. 构造新节点
    // 3. 将新节点的 prev 指针指向前驱节点(l)
    // 4. 将新节点的 next 指针指向后继节点(null)
    final Node<E> newNode = new Node<>(l, e, null);
    // 修改 last 指针
    last = newNode;
    if (l == null)
        // l 为 null 说明首个添加的元素,需要修改 first 指针
        first = newNode;
    else
        // 5. 将前驱节点的 next 指针指向新节点
        l.next = newNode;
    // 6. 将后继节点的 prev 指针指向新节点(后继节点是 null,所以没有这个步骤)
    size++;
    modCount++;
}

// 在指定节点前添加
// 1. 找到插入位置的后继节点
void linkBefore(E e, Node<E> succ) {
    final Node<E> pred = succ.prev;
    // 2. 构造新节点
    // 3. 将新节点的 prev 指针指向前驱节点(pred)
    // 4. 将新节点的 next 指针指向后继节点(succ)
    final Node<E> newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)
        first = newNode;
    else
        // 5. 将前驱节点的 next 指针指向新节点
        pred.next = newNode;
    size++;
    modCount++;
}

// 在指定位置添加整个集合元素
// index 为 0:在链表头部添加
// index 为 size:在链表尾部添加
public boolean addAll(int index, Collection<? extends E> c) {
    checkPositionIndex(index);
    // 事实上,c.toArray() 的实际类型不一定是 Object[],有可能是 String[] 等
    // 不过,我们是通过 Node中的item 承接的,所以不用担心 ArrayList 中的 ArrayStoreException 问题
    Object[] a = c.toArray();
    // 添加的数组为空,跳过
    int numNew = a.length;
    if (numNew == 0)
        return false;

    // 1. 找到插入位置的后继节点
    // pred:插入位置的前驱节点
    // succ:插入位置的后继节点
    Node<E> pred, succ;
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        // 找到 index 位置原本的节点,插入后变成后继节点
        succ = node(index);
        pred = succ.prev;
    }
    // 插入集合元素
    for (Object o : a) {
        E e = (E) o;
        // 2. 构造新节点
        // 3. 将新节点的 prev 指针指向前驱节点
        Node<E> newNode = new Node<>(pred, e, null);
        if (pred == null)
            // pred 为 null 说明是在头部插入,需要修改 first 指针
            first = newNode;
        else
            // 5. 将前驱节点的 next 指针指向新节点
            pred.next = newNode;
        // 修改前驱指针
        pred = newNode;
    }

    if (succ == null) {
        // succ 为 null 说明是在尾部插入,需要修改 last 指针
        last = pred;
    } else {
        // 4. 将新节点的 next 指针指向后继节点
        pred.next = succ;
        // 6. 将后继节点的 prev 指针指向新节点
        succ.prev = pred;
    }
    // 数量增加 numNew
    size += numNew;
    modCount++;
    return true;
}

// 将 LinkedList 转化为 Object 数组
public Object[] toArray() {
    Object[] result = new Object[size];
    int i = 0;
    for (Node<E> x = first; x != null; x = x.next)
        result[i++] = x.item;
    return result;
}

在链表中间添加节点时,会用到 node(index) 方法查询指定位置的节点。可以看到维持 first 和 last 头尾节点的作用又发挥出来了:

  • 如果索引位置小于 size/2,则从头节点开始找;
  • 如果索引位置大于 size/2,则从尾节点开始找。

虽然,我们从复杂度分析的角度看,从哪个方向查询是没有区别的,时间复杂度都是 O(n)。但从工程分析的角度看还是有区别的,从更靠近目标节点的位置开始查询,实际执行的时间会更短。

查询指定位置节点

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 寻找指定位置的节点,时间复杂度:O(n)
Node<E> node(int index) {
    if (index < (size >> 1)) {
        // 如果索引位置小于 size/2,则从头节点开始找
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        // 如果索引位置大于 size/2,则从尾节点开始找
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

LinkedList 的删除方法其实就是添加方法的逆运算,我们就不重复分析了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 删除头部元素
public E removeFirst() {
    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 remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

2.4 LinkedList 的迭代器

Java 的 foreach 是语法糖,本质上也是采用 iterator 的方式。由于 LinkedList 本身就是双向的,所以 LinkedList 只提供了 1 个迭代器:

  • ListIteratorlistIterator(): 双向迭代器

与其他容器类一样,LinkedList 的迭代器中都有 fail-fast 机制。如果在迭代的过程中发现 expectedModCount 变化,说明数据被修改,此时就会提前抛出 ConcurrentModificationException 异常(当然也不一定是被其他线程修改)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
    // 创建迭代器时会记录外部类的 modCount
    private int expectedModCount = modCount;

    ListItr(int index) {
        next = (index == size) ? null : node(index);
        nextIndex = index;
    }

    public E next() {
        // 更新 expectedModCount
        checkForComodification();
        ...
    }
    ...
}

2.5 LinkedList 的序列化过程

  • 🙋🏻‍♀️ 疑问 2:为什么字段都声明 transient 关键字?

LinkedList 重写了 JDK 序列化的逻辑,不序列化链表节点,而只是序列化链表节点中的有效数据,这样序列化产物的大小就有所降低。在反序列时,只需要按照对象顺序依次添加到链表的末尾,就能恢复链表的顺序。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 序列化和反序列化只考虑有效数据

// 序列化过程
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // Write out any hidden serialization magic
    s.defaultWriteObject();

    // 写入链表长度
    s.writeInt(size);

    // 写入节点上的有效数据
    for (Node<E> x = first; x != null; x = x.next)
        s.writeObject(x.item);
}

// 反序列化过程
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // Read in any hidden serialization magic
    s.defaultReadObject();

    // 读取链表长度
    int size = s.readInt();
  
    // 读取有效元素并用 linkLast 添加到链表尾部
    for (int i = 0; i < size; i++)
        linkLast((E)s.readObject());
}

2.6 LinkedList 的 clone() 过程

LinkedList 中的 first 和 last 指针是引用类型,因此在 clone() 中需要实现深拷贝。否则,克隆后两个 LinkedList 对象会相互影响:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private LinkedList<E> superClone() {
    try {
        return (LinkedList<E>) super.clone();
    } catch (CloneNotSupportedException e) {
        throw new InternalError(e);
    }
}

public Object clone() {
    LinkedList<E> clone = superClone();

    // Put clone into "virgin" state
    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;
}

2.7 LinkedList 如何实现线程安全?

有 5 种方式:

  • 方法 1 - 使用 Collections.synchronizedList 包装类: 原理也是在所有方法上增加 synchronized 关键字;
  • 方法 2 - 使用 ConcurrentLinkedQueue 容器类: 基于 CAS 无锁实现的线程安全队列;
  • 方法 3 - 使用 LinkedBlockingQueue 容器: 基于加锁的阻塞队列,适合于带阻塞操作的生产者消费者模型;
  • 方法 4 - 使用 LinkedBlockingDeque 容器: 基于加锁的阻塞双端队列,适合于带阻塞操作的生产者消费者模型;
  • 方法 5 - 使用 ConcurrentLinkedDeque 容器类: 基于 CAS 无锁实现的线程安全双端队列。

3. 总结

  • 1、LinkedList 是基于链表的线性表,同时具备 List、Queue 和 Stack 的行为;
  • 2、在查询指定位置的节点时,如果索引位置小于 size/2,则从头节点开始找,否则从尾节点开始找;
  • 3、LinkedList 重写了序列化过程,只处理链表节点中有效的元素;
  • 4、LinkedList 和 ArrayList 都不考虑线程同步,不保证线程安全。

在上一篇文章里,我们提到了 List 的数组实现 ArrayList,而 LinkedList 不仅是 List 的链表实现,同时还是 Queue 和 Stack 的链表实现。那么,在 Java 中的 Queue 和 Stack 的数组实现是什么呢,这个我们在下篇文章讨论,请关注。


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 彭旭锐 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java集合篇之深入解析LinkedList
作为ArrayList的同门师兄弟,LinkedList的师门地位逊色不少,除了在做算法题的时候我们会用到它之外,在实际的开发工作中我们极少使用它,就连它的创造者都说:“I wrote it,and I never use it”,想想颇有点好笑,但这并不影响我们去学习它,个人认为它底层的链表逻辑对于我们代码思想的培养还是挺有帮助的。
JavaBuild
2024/05/27
1210
Java集合篇之深入解析LinkedList
集合系列 List(四):LinkedList
从类继承结构图可以看到,LinkedList 不仅实现了 List 接口,还实现了 Deque 双向队列接口。
陈树义
2019/08/27
3540
集合系列 List(四):LinkedList
深入LinkedList,CopyOnWriteArrayList底层原理与源码解析
链表没有长度限制,他的内存地址不需要分配固定长度进行存储,只需要记录下一个节点的存储地址即可完成整个链表的连续。
用户8639654
2021/07/23
3150
LinkedList 源码解析
LinkedList 是 Java 集合框架中一个重要的实现,其底层采用的双向链表结构。和 ArrayList 一样,LinkedList 也支持空值和重复值。由于 LinkedList 基于链表实现,存储元素过程中,无需像 ArrayList 那样进行扩容。但有得必有失,LinkedList 存储元素的节点需要额外的空间存储前驱和后继的引用。另一方面,LinkedList 在链表头部和尾部插入效率比较高,但在指定位置进行插入时,效率一般。原因是,在指定位置插入需要定位到该位置处的节点,此操作的时间复杂度为O(N)。最后,LinkedList 是非线程安全的集合类,并发环境下,多个线程同时操作 LinkedList,会引发不可预知的错误。
HLee
2021/05/11
3860
LinkedList 源码解析
面试官:LinkedList 真的是查找慢、增删快?
废话不多说,先上测试结果。作者分别在 ArrayList 和 LinkedList 的头部、尾部和中间三个位置插入与查找 100000 个元素所消耗的时间来进行对比测试,下面是测试结果。
南风
2019/12/19
2.3K0
面试官:LinkedList 真的是查找慢、增删快?
LinkedList源码和并发问题分析
1.LinkedList源码分析 LinkedList的是基于链表实现的java集合类,通过index插入到指定位置的时候使用LinkedList效率要比ArrayList高,以下源码分析是基于JDK
代码改变世界-coding
2018/07/03
8470
死磕 java集合之LinkedList源码分析
LinkedList是一个以双向链表实现的List,它除了作为List使用,还可以作为队列或者栈来使用,它是怎么实现的呢?让我们一起来学习吧。
彤哥
2019/07/08
3360
死磕 java集合之LinkedList源码分析
Java LinkedList 简单源码分析节选
这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解。所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项目,专注 Java 后端面试题 + 解析 + 重点知识详解 + 精选文章的开源项目,希望它能伴随你我一直进步!
BWH_Steven
2021/03/15
3300
面试官:兄弟,说说 ArrayList 和 LinkedList 有什么区别
ArrayList 和 LinkedList 有什么区别,是面试官非常喜欢问的一个问题。可能大部分小伙伴和我一样,能回答出“ArrayList 是基于数组实现的,LinkedList 是基于双向链表实现的。”
沉默王二
2020/09/28
6680
面试官:兄弟,说说 ArrayList 和 LinkedList 有什么区别
LinkedList 源码分析(JDK 1.8)
LinkedList 是 Java 集合框架中一个重要的实现,其底层采用的双向链表结构。和 ArrayList 一样,LinkedList 也支持空值和重复值。由于 LinkedList 基于链表实现,存储元素过程中,无需像 ArrayList 那样进行扩容。但有得必有失,LinkedList 存储元素的节点需要额外的空间存储前驱和后继的引用。另一方面,LinkedList 在链表头部和尾部插入效率比较高,但在指定位置进行插入时,效率一般。原因是,在指定位置插入需要定位到该位置处的节点,此操作的时间复杂度为O(N)。最后,LinkedList 是非线程安全的集合类,并发环境下,多个线程同时操作 LinkedList,会引发不可预知的错误。
田小波
2018/04/26
6920
LinkedList 源码分析(JDK 1.8)
【震精】LinkedList源码竟然可以这样玩!!
注意一点LinkedList并没有实现RandomAccess所以随机访问是非常慢的。
乱敲代码
2019/06/20
3960
【震精】LinkedList源码竟然可以这样玩!!
LinkedList源码学习
之前学习了ArrayList,了解了其基于数组的本质,那么LinkedList是怎么实现的?显然LinkedList是链表。也就是基于链表实现。链表分为单向链表和多向链表。那么LinnkedList具体是那种类型的链表?我们可能在工作中一直在用但是也许对LinkedList的原理不熟悉。怀着疑问,我们来解析一下吧!
写一点笔记
2020/09/01
3940
LinkedList源码学习
走进 JDK 之 LinkedList
如果你了解链表的基本结构的话,LinkedList 的源码其实还是比较容易理解的。LinkedList 是基于双向链表实现的,与 ArrayList 不同的是,它在内存中不占用连续的内存空间,相连元素之间通过 “链” 来链接。对于单链表,每个节点有一个 后继指针 指向下一个节点。对于双向链表来说,除了后继指针外,它还要一个 前驱指针 指向前一个节点。那么,双向链表有什么好处呢?既然有了前驱指针,在遍历的时候就可以向前遍历,在下面的源码分析中可以看到,这是单链表所不具备的功能。
路遥TM
2021/08/31
2810
深度解析LinkedList
LinkedList是Java集合框架中List接口的实现之一,它以双向链表的形式存储元素。与传统的数组相比,链表具有更高的灵活性,特别适用于频繁的插入和删除操作。让我们从底层实现开始深入了解这个强大的数据结构。
修己xj
2023/12/26
2790
深度解析LinkedList
啃透JDK源码-LinkedLis
与 ArrayList 一样实现了 List 接口,只是 LinkedList 底层结构为链表.在插入和删除时更优于 ArrayList,而随机访问则比 ArrayList 稍逊.
JavaEdge
2020/05/26
4710
死磕Java之聊聊LinkedList源码(基于JDK1.8)
我们主要看研究一下下面的几个方法,LinkedList其他方法都是通过调用这几个方法来实现功能,包括LinkedList的双端队列的方法也是。
haifeiWu
2018/09/11
4210
死磕Java之聊聊LinkedList源码(基于JDK1.8)
你不知道的LinkedList(一):基于jdk1.8的LinkdeList源码分析
在对ArrayList源码有过了解之后,现在对LinkedList源码进行相应的分析。
冬天里的懒猫
2020/08/17
4400
LinkedList源码分析
LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性;
Vincent-yuan
2021/09/08
3870
LinkedList源码解析
LinkedList是一个实现了List接口和Deque接口的双端链表。LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性; LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法:
黑洞代码
2021/01/28
9580
LinkedList源码解析
jdk1.8源码阅读LinkedList
LikedList实现采用了双向链表,并且LinkedList实现了Dqueue接口,所以LinkedList可以当作普通队列和双端队列使用,首先看一下LinkedList的类关系图
用户4415180
2022/06/23
2310
jdk1.8源码阅读LinkedList
相关推荐
Java集合篇之深入解析LinkedList
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档