【问题标题】:Why doesn't Collections.copy increase the size of the destination?为什么 Collections.copy 不增加目标的大小?
【发布时间】:2014-02-24 11:43:20
【问题描述】:

我正在使用以下代码:

List<Object> dest = new LinkedList<Object>();
Collections.copy(oldList, src);

当我执行代码时,我得到一个异常:

java.lang.IndexOutOfBoundsException: Source does not fit in dest
    at java.util.Collections.copy(Collections.java:589)

我知道如何解决这个问题,但我不明白的是:

当我手动添加元素时,列表会自动增加容量。为什么 Collections#copy 不能这样做?

【问题讨论】:

    标签: java list collections


    【解决方案1】:

    正如 StinePike 在https://stackoverflow.com/a/21986805 中所说:这就是它的实现方式。它只是一种方便的方法,您可以使用它而不是像这样的 for 循环

    for (int i=0; i<src.size(); i++)
    {
        dst.set(i, src.get(i));
    }
    

    关于您关于自动大小增加的问题:听起来您实际上正在寻找类似 @​​987654323@ 的东西 - 所以在这种情况下

    dest.addAll(src);
    

    【讨论】:

      【解决方案2】:

      因为这就是它的实现方式。如果你看到这个method 的源代码,你就会看到

       if (srcSize > dest.size())
          throw new IndexOutOfBoundsException("Source does not fit in dest");
      

      所以如果要实现自增,那就自己写方法吧。

      【讨论】:

      • 但他们为什么要这样做?这并不是一个真正令人满意的答案。我没有看到任何好处
      • 这是一个正确的答案。该方法称为 copy 并执行文档中指定的操作:它复制元素。没有任何地方提到对目的地的修改。
      • 不,这个答案严格来说是不正确的。该行为存在,因为这就是 API 文档中定义该方法的方式。不是因为某些供应商决定在 JVM 的任意实现中放入几行代码。
      • 如果您阅读了我的回答的第一行 .. 因为这就是它的实现方式...我粘贴此实现只是为了展示一个实现...。无论如何感谢您的反对(y)
      • @StinePike:我读了你答案的第一行,这正是我要批评的那一行。 Phillip 看到的行为在 API 文档中定义。正如我所说,查看 API 的任意实现并扣除对该方法所做或应该做的任何假设总是一个坏主意。
      【解决方案3】:

      Collections#copy() 是一种实用方法,适用于任何List。此方法旨在仅重用现有数组而不分配任何内存。所以,这可能是有效的,否则你可能会使用 ArrayList(Collection) 构造函数,它会为你完成扩展工作。大小约束和扩展仅与 ArrayList 相关的事实也可能是一个原因,而不是将此行为添加到方法中。为了特定的List 实现,这只是额外的代码。这不是接口编程的工作原理。

      而且由于文档中明确提及,他们希望您遵循它。

      【讨论】:

      • List 接口有 addaddAll 方法来展开列表。
      • @jarnbjo rohit 的意思是扩大尺寸但不影响内容的方法
      • @jarnbjo 好吧,我不是这个意思。但仍然更新了答案。关键是没有正确的答案。
      • @PhillipSander:这可能是他的意思,但在这种情况下为什么要相关?如果复制方法会扩展目标列表以适应整个源列表,它可以简单地通过使用add 方法添加额外元素而不是用set 替换它们来完成。
      • @jarnbjo 用一些明智的答案更新了答案。可以看看。
      【解决方案4】:

      如果您看到 @987654321@ 方法的实现,它会在 sourcedestination 中维护 相同的索引 > 列表,复制操作后。

      要在索引处设置元素,应使用set(int) 方法。 set(int) 方法期望索引存在于目标列表中(与 add(Object) 不同,它会在没有容量时扩展列表)。

      想象一下,如果这个方法在复制时使用了add(Object),并且dest 列表已经有一些元素,那么新元素(来自src)将被添加到dest 列表的末尾,打破 复制合约(这意味着相同元素@各自相同的索引

      因此,如果目标列表没有源列表那么长,该方法将引发异常

      【讨论】:

      • 鉴于 source 大于 dest,确实可能以使用set(index) 替换现有元素并使用add 添加更多元素的方式实现它.
      • 你是对的,但在概念上它与任何其他副本相同。考虑将文件从一个驱动器复制到另一个驱动器。操作系统不会扩展目标驱动器以容纳源文件,而是抱怨..也许我们可以这样想:)
      【解决方案5】:
      【解决方案6】:

      您假设,List 的大小是可变的,Collections.copy() 不假设这是为了在理论上将此类的目的扩展到所有实现 List 接口的类,而不仅仅是可变的。

      【讨论】:

      • 任何关于超出固定大小集合容量的异常都可以由该集合处理。此实用程序方法不需要强制执行。事实是,确实如此,但 OP 质疑为什么 - 这个答案没有提供他所寻求的推理。
      • 您正在反转问题,实用程序方法不会强制执行任何有关可变性的事情,而是说明要复制到的集合至少与源集合一样大 - 如果实用程序方法会要求一个集合是可变的,它会强制执行一些东西。
      • 提出了可变性作为原因,而不是我。 :)
      • 不,OP 假设所有实现列表的类的集合大小都被该方法视为可变的。
      • 如果这不成立,并且add 会失败,那么当需要导致大小的操作时,目标列表会抛出该异常增加发生 - 它不需要由实用程序方法强制执行。
      猜你喜欢
      • 2015-12-06
      • 1970-01-01
      • 2015-07-17
      • 2011-06-13
      • 1970-01-01
      • 2021-07-03
      • 2018-05-10
      • 2019-10-15
      • 2010-12-28
      相关资源
      最近更新 更多