【手撕Java集合】关于 LinkedList 你可能还不知道!

本文深入剖析了LinkedList的属性、构造方法、add、remove和get/set操作,以及与ArrayList的性能对比,重点讲解了链表结构和其在增删查改中的优势与劣势。

LinkedList 源码剖析

类图:

在这里插入图片描述

LinkedList 底层是双向链表

在这里插入图片描述

可以看到,LinkedList 实现了 Deque 接口,所以可以像操作队列和栈一样操作 LinkedList:

在这里插入图片描述

1. LinkedList 的属性

LinkedList 共有4个属性:

在这里插入图片描述

属性解析:

在这里插入图片描述

// 序列化版本UID
private static final long serialVersionUID = 876323262645176354L;

2. 构造方法

LinkedList 有一个空参构造器和一个有参构造器:

在这里插入图片描述

3. 添加方法 add()

add方法实际上就是往链表最后添加元素

public boolean add(E e) {
    linkLast(e);//往链表尾端加元素的方法
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;//先把尾元素赋给l
    final Node<E> newNode = new Node<>(l, e, null);//调用内部类Node的有参构造器构造一个新结点
    last = newNode;//再把新结点的引用赋给last
    if (l == null)
        first = newNode;//如果原本的尾元素为空,则将新结点赋给首元素,此时链表只有一个元素(首尾)
    else
        l.next = newNode;//否则将新元素赋给原尾元素的下一个位置
    size++;//元素数量+1
    modCount++;//链表修改次数+1
}

//内部类Node
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;
        }
    }

4. 移除方法 remove()

在这里插入图片描述

再来看看unlink()方法的内部逻辑:

在这里插入图片描述

上述代码进行的就是如下的操作:

在这里插入图片描述

5. 查找方法 get()

public E get(int index) {
    checkElementIndex(index);//验证索引的合法性
    return node(index).item;
}

//返回指定索引上的非空元素
Node<E> node(int index) {
        // assert isElementIndex(index);能执行到node()方法,认定索引是合法的

        if (index < (size >> 1)) {//用到类似二分查找思想,若index < size的一半,则从首元素开始找
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {//若index ≥ size的一半,则从尾元素开始向前找
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

6. set() 方法

set方法和get方法差不多,根据下标来判断是从头遍历还是从尾遍历

public E set(int index, E element) {
    checkElementIndex(index);//验证索引合法性
    Node<E> x = node(index);//查找元素
    E oldVal = x.item;
    x.item = element;
    return oldVal;//将旧元素上的值返回
}

7. ArrayList 和 LinkedList 对比

  • ArrayList 底层是数组,LinkedList 底层是双向链表
  • 两者都是非同步的(线程不安全)
  • 对比 ArrayListLinkedList 的 API 的时间复杂度:
功能方法ArrayListLinkedList
add(E e)O(1)O(1)
add(int index, E e)O(n)O(n)
remove(int index)O(n)O(n)
remove(E e)O(n)O(n)
set(int index, E e)O(1)O(n)
get(int index)O(1)O(n)

结论:

  1. 改查选择 ArrayList;
  2. 增删在尾部的选择 ArrayList,增删在中间位置也选择 ArrayList;
  3. 其他情况下,如果时间复杂度一样,推荐选择 ArrayList,因为 overhead 更小,或者说内存使用更有效率。
  4. 但一般来说,增删多还是用 LinkedList,因为上面的情况比较极端
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kaho Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值