繁体   English   中英

从列表中获取/删除第一个元素的有效方法?

[英]Efficient way to get/remove first element from the list?

我想从列表中取出并删除第一个元素。 我可以看到,我有两个选择:

第一种方法:

LinkedList<String> servers = new LinkedList<String>();
....
String firstServerName = servers.removeFirst();

第二种方法

ArrayList<String> servers = new ArrayList<String>();
....
String firstServerName = servers.remove(0);

我的列表中有很多元素。

  • 我们应该使用哪一种?
  • 而以上两者有什么区别呢? 在性能方面,它们在技术上是否相同? 如果我们有很多元素,这里涉及的复杂性是什么?

什么是最有效的方法来做到这一点。

如果“先删除”的比较是在ArrayListLinkedList类之间进行,则LinkedList明显胜出。

从链表中删除元素的成本为O(1) ,而对数组(数组列表)执行此操作的成本为O(n)

确保您了解 LinkedList 和 ArrayList 之间的区别。 ArrayList 是使用 Array 实现的。

LinkedList 需要恒定的时间来删除元素。 ArrayList 可能需要线性时间来删除第一个元素(确认我需要检查实现,而不是这里的 java 专家)。

另外我认为 LinkedList 在空间方面更有效。 因为 ArrayList 不会(也不应该)在每次删除元素时重新调整数组的大小,所以它占用的空间比需要的多。

实际上, LinkedList#removeFirst效率更高,因为它在双向链表上运行,并且删除第一个元素基本上只是将它与列表的头部断开链接并将下一个元素更新为第一个:

private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

ArrayList#remove正在对内部数组进行操作,该数组需要通过复制子数组将所​​有后续元素向左移动一个位置:

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

另一方面, LinkedList#get操作需要遍历整个列表的一半以检索指定索引处的元素 - 在最坏的情况下。 ArrayList#get将直接访问指定索引处的元素,因为它对数组进行操作。

我在这里提高效率的经验法则是:

  • 如果与检索操作(例如: get )相比,您做了很多add / remove操作,请使用LinkedList
  • add / remove相比,如果您执行大量检索操作,请使用ArrayList

我认为你需要的是一个ArrayDequejava.util一个被不公平地忽视的类)。 它的removeFirst方法与LinkedList一样在 O(1) 中执行,而它通常显示出ArrayList更好的空间和时间特性。 它被实现为数组中的循环队列。

您应该很少使用LinkedList 我在 17 年的 Java 程序员生涯中做过一次,现在回想起来很后悔。

List.subList (int fromIndex, int toIndex)

返回此列表中指定的 fromIndex(包括)和 toIndex(不包括在内)之间的部分的视图。

非常适合用于ArrayList ,其中删除第一个元素的复杂度为 O(n)。

final String firstServerName = servers.get(0);
servers = servers.subList(1, servers.size());

使用链表要快得多。

链表

它只会引用节点,因此第一个节点会消失。在此处输入图片说明

数组列表

对于数组列表,它必须将所有元素移回一个位置以保持底层数组正确。

删除 ArrayList 的第一个元素是 O(n)。 因为链表是 O(1),所以我会这样做。

检查 ArrayList 文档

size、isEmpty、get、set、iterator 和 listIterator 操作在恒定时间内运行。 add 操作在分摊常数时间内运行,即添加 n 个元素需要 O(n) 时间。 所有其他操作都在线性时间内运行(粗略地说) 与 LinkedList 实现相比,常量因子较低。

这家伙实际上得到了 OpenJDK 源代码链接

正如其他人正确指出的那样,LinkedList 比 ArrayList 更快,可以从非常短的列表以外的任何内容中删除第一个元素。

但是,要在它们之间做出选择,您需要考虑完整的操作组合。 例如,如果您的工作负载在每次删除第一个元素时对一百个元素列表进行数百万次索引访问,则 ArrayList 总体上会更好。

第三个方法。

它由 java.util.Queue 接口公开。 LinkedList 是这个接口的一个实现。

Queue 接口公开了 E poll() 方法,该方法有效地删除了列表(队列)的头部。

在性能方面, poll() 方法与 removeFirst() 相当。 实际上它在幕后使用 removeFirst() 方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM