【问题标题】:Which Java collection allows cheap appending?哪个 Java 集合允许廉价追加?
【发布时间】:2012-09-30 21:23:52
【问题描述】:

我正在寻找一种 Java 中的列表数据结构,它允许廉价地附加长列表。我尝试了 LinkedList,但我在addAll 的文档中发现,使用迭代器来附加两个列表。这意味着附加的列表在操作期间被克隆。迭代器遍历整个列表,返回每个元素。是否有任何可用的集合在附加两个列表时省略了迭代?

【问题讨论】:

  • 您的意思是要在列表之间共享结构?
  • 我不是 Java 专家,但我认为这不适合 Java 集合 API,因此您需要寻找第三方库(或自行开发)。跨度>
  • 这个问题与你的问题有关:stackoverflow.com/questions/2237308/…,在任何 JDK 集合中都没有类似 STL 的 list::splice 方法
  • 如果结果列表需要只读,请看:stackoverflow.com/q/4896662/100836
  • @Zaki:嗯,这是我第二次点击Guava。我第一次能够在不需要 Guava 的情况下解决问题。但这一次似乎很难。

标签: java list


【解决方案1】:

并非如此,因为所有“追加”操作都不会对基础集合做出任何假设。从技术上讲,两个链表可以直接附加,但附加必须是通用的,因此它使用迭代。

另一个不允许直接串联的好理由是,在追加更改后一个列表也会影响另一个,我敢肯定这不是一个理想的属性。

【讨论】:

  • 标准库还有其他方法吗?
  • @ceving:我猜你可以继承LinkedList 并提供你自己的实现。
【解决方案2】:

ArrayList 中的addAll 将其转换为数组,然后使用系统调用进行复制(System.arraycopy)。这应该比在 java 中循环更快,因为它是原生的,我认为没有便宜的 appender。

【讨论】:

  • 不这么认为。如果列表的长度足够长,则没有太大区别。
【解决方案3】:

即使你确实找到了这样一个集合类,你也不能保证它会在未来的 Java 版本中继续以这种方式工作。这是因为这是 Collection 接口中未指定的行为,因此受制于类开发人员的突发奇想。

当然,您可以编写自己的集合类,但即便如此,您也会对其他集合类的行为做出假设

【讨论】:

    【解决方案4】:

    可能是 ArrayList 的 addAll 应该更快,因为它不迭代,而是使用 System.arrayCopy。代码看起来像

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
            int numNew = a.length;
        ensureCapacity(size + numNew);  // Increments modCount
            System.arraycopy(a, 0, elementData, size, numNew);
            size += numNew;
        return numNew != 0;
        }
    

    【讨论】:

      【解决方案5】:

      AbstractCollectionsaddAll 方法的默认实现使用迭代器添加addAll 方法在提供自定义特定实现的子类中被覆盖

      喜欢

      • ArrayList 使用System.arraycopy
      • LinkedList 使用循环遍历元素。

      【讨论】:

      • 是的,我知道。我正在寻找第三个,它既不复制也不循环。
      【解决方案6】:

      我认为标准 API 没有办法,但根据您的需要,您可以尝试自己实现一个。例如,您可以实现一个内部有一个集合引用列表,每次调用 addAll 方法时添加。

      【讨论】:

        【解决方案7】:

        您可以使用Guava's Iterables.concat 方法创建连接的Iterable View..

        Iterable<T> combined = Iterables.concat(list1, list2);
        
        • 不会将元素复制从一个列表到另一个列表..
        • 因此,它不会改变您的任何列表..
        • 另外,这不会创建一个新列表(它会创建一个Iterable,它不是一个列表)

        基本上,它会创建一个Iterable,通过它您可以背靠背迭代two 列表(它从list1 迭代元素,然后从list2 迭代元素。..

        注意:- 如果您想要一个列表作为two lists 的串联,那么这可能对您没有多大帮助.. 因为,它不会创建列表,但是一个 Iterable.. 对于这种情况,除了 Iterating 在您的列表和 copy 您的每个参考之外,您别无选择..

        来自docs:-

        它将两个可迭代对象组合成一个可迭代对象。返回的可迭代 有一个遍历 a 中元素的迭代器,后跟 b中的元素。直到必要时才会轮询源迭代器。 返回的迭代器的迭代器支持 remove() 当 对应的输入迭代器支持。

        你也有这个方法的var-args版本..See Docs..这可以接受任意数量的列表,并返回可以按顺序迭代这些列表的Iterables..所以,你可以这样做..

        Iterable<T> combined = Iterables.concat(list1, list2, list3, list4, ...);
        

        此链接 --> google-guava-libraries-essentials 您可能也感兴趣..

        【讨论】:

        • 确实更快...是 O(n) 吗?它必须找到一个列表的结尾
        • 确实这样更快。它不必找到结尾..它已经有拖链列表..连接是 O(1)..
        • 您也可以使用此方法组合multiple 列表。请参阅此方法的Var-arg 版本:-docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
        • 真的有必要使用Guava吗? Java 的标准库没有提供这么简单的功能,这对我来说听起来很奇怪。甚至Glib 也有。
        • Iterable 不是“列表数据结构”这一点毫无价值。要将其变成一个,您必须复制所有元素,因此它并不能真正为您节省任何东西。
        【解决方案8】:

        这意味着被附加的列表在操作期间被克隆。

        创建迭代器不会克隆集合。

        addAll 方法在大多数情况下调用 toArray() 以确保自动提取数据,从而将集合的元素克隆为数组,这很可能使用 Iterator 来完成。

        是否有任何可用的集合在附加两个列表时省略迭代?

        不,但如果这真的很重要,您可以自己迭代集合。


        最有效率的可能是

        List<E> list1 = ... random access list ...
        List<E> list2 = ... random access list ...
        
        for(int i = 0; i < list2.size(); i++)
            list1.add(list2.get(i));
        

        这不会创建任何对象并且有O(n) 时间,其中n 是list2 的大小。

        【讨论】:

        • 我不想迭代也不想复制。
        • 您必须至少复制参考资料。你可以通过循环来做到这一点。
        • @ceving '不想要 this 也不想要 that' :) 那么 what 你呢真的想实现吗?
        • @Durandal:快速连接。快速意味着:没有复制也没有循环。
        • 唯一的方法是创建一个自定义列表,它是两个原始列表的视图。可以做到,但如果有两个以上的列表,就会变得越来越复杂。
        【解决方案9】:

        使用这种行为,添加到第一个列表的列表将被消耗(即修改)或导致第二个列表成为具有各种陷阱的较大列表的子列表(例如,如果您添加两次列表会发生什么...... .),

        addAll 有一个隐含的 const 参数(这意味着第二个列表将在多次调用中保持不变),因此除非您自己滚动,否则此行为将不存在

        【讨论】:

          【解决方案10】:

          我会简单地使用 ArrayList,然后愉快地将我的东西添加到它。选择的 List 实现如何执行 addAll() 操作并不重要,迭代或 toArray 都相对便宜,而且这不太可能成为性能瓶颈(假设您的应用程序确实有用使用列表元素,而不仅仅是创建列表...)。

          在 addAll() 中的迭代确实没有办法,如果你想要一个包含 N 个源列表中的元素的独立列表,则元素引用 必须 被复制到某个地方(你可以更改addAll() 之后的源列表,但不能影响已连接的列表,因此复制是不可避免的)。

          如果您真的需要 List 语义(独立于源),那么利用已经建议的 Guava 创建多个列表的 view(或自己动手,如果你不想要依赖,这不是火箭科学)。

          【讨论】:

            猜你喜欢
            • 2016-06-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-10-01
            • 2012-06-28
            • 1970-01-01
            • 2013-01-23
            相关资源
            最近更新 更多