【问题标题】:How does add(index, element) method work behind the scenes using LinkedList?add(index, element) 方法如何使用 LinkedList 在幕后工作?
【发布时间】:2020-01-23 16:55:19
【问题描述】:

当使用 ArrayList 中的 add(index, element) 方法在某个索引处添加元素时,它将元素放置在该索引处,而所有其他元素将它们的索引更改为 1(它们在内存中移动)。这就是 ArrayList 在特定位置添加元素时具有 O(n) 复杂度的原因。

如果是双重LinkedList,我知道元素有指向前一个元素的指针,也有指向下一个元素的指针。

我的问题是,当使用 LinkedList 相关的 add(index, element) 方法时,幕后实际会发生什么?我知道使用 LinkedList 其余元素不会在内存中移动,那么为什么它们仍然可以放置在某个索引处而不在内存中移动?

【问题讨论】:

  • LinkedList 上的索引与连续的内存位置不对应。相反,get() 方法需要遍历列表才能访问正确的索引。这就是为什么ArrayList::get 的复杂度为 O(1)(它可以直接跳转到正确的内存地址),而LinkedList::get 的复杂度为 O(n)。

标签: java doubly-linked-list


【解决方案1】:

向链表中指定索引处添加一个新元素需要遍历链表(从头部或尾部),然后在新元素中拼接source code 对应于 LinkedList#add(int index, E element) 揭示了很多:

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

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

如果索引指向列表中的最后一项,它只是在末尾附加一个新节点。否则,它会调用linkBefore(),它会做一些拼接工作(我也不会费心包含它的源代码)。

请注意,向链表添加新节点并不一定涉及移动现有链表中的任何内容。相反,它主要涉及在后台移动引用。

【讨论】:

  • @Michael 我真的很担心有人会为此打电话给我。我稍微改变了语言。
  • 欢迎来到学究国
【解决方案2】:

add(index, element) 的实现如下所示:

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

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

如果要在LinkedList 的尾部添加元素,则linkLast 方法可以在恒定时间内执行; LinkedList 始终可以直接访问其最后一个元素,无需遍历。

否则,node 方法很昂贵,因为最多需要遍历列表的一半:

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

元素不需要在内存中移动,因为它们每个都引用LinkedList 中的上一个和下一个节点,如下面的linkBefore 所示:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 2016-12-31
    • 1970-01-01
    • 1970-01-01
    • 2016-10-20
    • 1970-01-01
    相关资源
    最近更新 更多