Java 集合—— LinkedList

Java 集合—— LinkedList

介绍

  从名字上可以看出,它与 ArrayList 一样,都是 List 的具体实现子类,但它是基于链表的,链表这种数据结构相信只要学过编程或者计算机的,都应该熟悉这种数据结构,那么接下来,我将从 LinkedList 源码的角度来解读 LinkedList 对象是如何实现的。

LinkedList 定义

  以下为 LinkedList 的部分定义,它保存了链表的头和尾节点,并且它是一个双向链表,list 的基本操作都是通过对链表进行操作来实现的,现在我们来具体分析基本操作的过程。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, 
    java.io.Serializable {

    transient Node<E> first;
    transient Node<E> last;

    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;
        }
    }
}

基本操作

add 操作

  调用 add(index,element); 方法,实际调用流程:
1. 首先会先判断当前插入的 index 位置是否合法(即是否大于当前元素个数),若不合法,则会抛出位置越界异常;否则执行 2
2. 若下标合法,则判断当前 index 是否为末尾,若是,直接调用 linkLast 方法在表尾插入元素,然后结束;否则执行 3
3. 插入的位置在链表中间,则它会找到插入位置的节点(这里为了提高查询速度,在查询时,先判断 index 在前半段还是在后半段,若在前半段则从 first 开始找;反之从 last 开始回退找),然后将该元素插入该位置,修改对应指针指向。

从上述过程可以看出,如果在表尾插入元素,则时间复杂度为 O(1);如果是表的其他位置插入元素,则时间复杂度为 O(n),因为要遍历链表,最优情况只比较一个元素,最坏情况需要比较 n/2 个元素,因此时间复杂度为 O(n)。

以下为源码实现:

    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    // 插入到表尾
    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++;
    }

    // 插入到 succ 节点前面
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

    // 获取 index 位置的节点
    Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

remove 操作

  remove 操作有两种,一种是 remove(object),一种是 remove(index),前者是删除表里面第一个与 object 相等的节点元素,后者是删除 index 位置节点元素

remove(object)
  该方式从 first 开始遍历链表,找到第一个与 obejct 相等的元素,找到则直接将该元素从链表上移除。但需要分两种情况,一种情况是 object 为 null 的情况,此时用 == 运算符来比较;另一种是 object 不为 null 的情况,此时用 equals 方法来比较。

    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;
    }

remove(index)
  该种方式先判断 index 位置是否合法,合法则直接将改位置的元素移出链表(修改前后节点的链表指向即可)

    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

Iterator

  LinkedList 的 迭代器与 ArrayList 一样,都是属于 fast-fail 类型,当在遍历过程时,别的线程修改了集合元素,则会抛出 ConcurrentModificationException。

总结

 LinkedList,与 ArrayList 和 Vector 一样,都是 List 的实现子类,前者是基于链表实现,而后者是基于动态数组实现,因此不需要像数组那样考虑扩容操作,因此适合于元素个数不确定的情况。
 在 LinkedList 中,它采用双向链表的形式,并且保存了表头和表尾节点。对于插入操作,如果在表尾或在表头插入元素,则时间复杂度为 O(1),若在表中间插入元素,则需要遍历链表找到对应位置的节点再插入元素,此时时间复杂度为 O(n)。
 在遍历寻找元素的过程,由于是双向链表并且保存了头和尾节点,因此为了减少比较次数,先通过判断 index 位于前半段还是后半段,前半段则从 first 开始查找,后半段则从 last 开始查找。
 另外 LinkedList 和 ArrayList 都是线程不安全,而 Vector 是线程安全;它们 Iterator 迭代器都属于 fast-fail 类型,若别的线程对集合进行修改(删除、插入),则会导致抛出 ConcurrentModificationException,要想使这两个集合变成线程安全,则可以Collections.synchronizedList(list);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值