深入分析LinkedList实现原理

本文详细解析了LinkedList的数据结构特点及其在插入和删除操作上的高效性。对比ArrayList,LinkedList采用链式存储结构,适合频繁进行元素增删的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LinkedList 顾名思义是一个列表。与上一遍中的ArrayList相比,LinkedList的存储结构是链式的,非连续存储。如此它在插入,删除元素方面的效率强于ArrayList,但是随机访问和遍历的效率弱于ArrayList

简单应用

import java.util.LinkedList;

public class LinkedListStudy {
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<String>();
        list.add("001");
        list.add("002");
        list.add("003");
        System.out.println(list);
        list.remove("001");
        System.out.println(list);
        list.add(2, "005");
        System.out.println(list);
    }
}

输出

[001, 002, 003]
[002, 003]
[002, 003, 005]

查看API文档发现,LinkedList只有俩个构造方法:

LinkedList()
LinkedList(Collection<? extends E> c)

不同于ArrayList,LinkedList不可以在初始化时候指定大小。同样ArrayList有加载因子,在容量不够的时候可以扩容,然而LinkedList容量的说法,每次向其中加入元素时候,容量自动加1。

当我们调用add(E e)方法时候发生了什么?

查看源码

/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
*            (first.prev == null && first.item != null)
*/
transient Node<E> first;

/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
*            (last.next == null && last.item != null)
*/
transient Node<E> last;
public boolean add(E e) {
        linkLast(e);
        return true;
    }
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++;
    }
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;
        }
    }

由此可知,当我们向容器添加一个元素的时候,首先它会将我们加入的元素包装在一个内部类Node里面,这个Node里面包含了它的左右邻居,左邻居即为当前的最末元素,右邻居为空元素,之后将加入的元素放在最末尾,并且记录向前容器内的首尾元素,然后容器的容量值加1。

当我们调用remove(Object o)时候发生了什么呢

查看源码

public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

由此可知,调用remove后,容器首先在内部找到传入的元素,然后调用unlink方法使得当前的元素的左右邻居连接起来,并且将当前元素置空。通过以上的分析你知道为什么LinkedList的插入,删除效率高于ArrayList了吗?因为ArrayList的底层为数组实现,每次更改都需要把整个数据复制,而LinkedList只需要把元素的左右邻居指针换掉就可以了。

LinkedList的方法众多,本文就只分析最常见的俩个操作。剩下的小伙伴们自己探索哦。博主知识水平有限,如有错误之处,还望小伙伴们提出错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值