【问题标题】:Java 8 ArrayList initial capacity broken?Java 8 ArrayList 初始容量坏了?
【发布时间】:2016-07-13 09:41:36
【问题描述】:

今天,当我遇到一个奇怪的问题时,我正在寻找克隆 ArrayList。 我读了this 的答案并尝试了。但是,我收到了 ArrayOutOfBounds 异常。所以我仔细研究了一下,显然 ArrayList(int size) 不起作用?
这是一个已知问题吗?

测试类:

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class ArrayListTest {
    @Test
    public void test() {
        List<String> lista = new ArrayList<>();
        lista.add("a");
        List<String> listb = new ArrayList<>(lista.size());
        System.out.println("Size of lista: " + lista.size());
        System.out.println("Size of listb: " + listb.size());
    }
}

有问题的班级:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.junit.Test;

public class ArrayListTest2 {
    @Test
    public void test() {
        List<String> lista = new ArrayList<>();
        lista.add("a");
        List<String> listb = new ArrayList<>(lista.size());
        Collections.copy(listb, lista);
        System.out.println("Lista: " + lista);
        System.out.println("Listb: " + listb);
    }
}

结果:

java.lang.IndexOutOfBoundsException:源不适合目标
在 java.util.Collections.copy(Collections.java:556)
在 ArrayListTest2.test(ArrayListTest2.java:13)

有什么想法吗?

【问题讨论】:

  • 你知道,如果一个被广泛使用了 10 年的方法被破坏到根本不起作用,你不认为人们现在会发现它吗?我的意思是您不是在寻找 Java 中的错误。您应该首先查看 your 代码中的错误。再次阅读文档。尤其是构造函数new ArrayList(size)。看到它分配内存,它不插入元素。之后大小仍然为0。
  • 好的,我明白了。但是为什么副本不起作用?当然,我并不是假设我在 Java 中发现了一个错误。但我只是好奇我是否确实做出了错误的假设,或者发生了其他事情。
  • 再次,您是否阅读过copy 的文档?有一部分说目标列表必须至少与源列表一样长
  • @the8472 追踪 JDK 源代码是恕我直言,对 SO 提问者提出了太多要求。事实上,我们收到了来自对完全相同的事情感到困惑的人关于Collections.copy() 的错误报告。这就是我们修复文档的原因。
  • 期望人们阅读 Java SE 类的源代码不仅是不合理的,而且这样做与面向对象的开发完全相反。一个人应该遵循一个类的契约,而不是它的实现。

标签: java arraylist java-8


【解决方案1】:

这确实是Collections.copy()的文档有问题。它的JDK 8 documentation 说:

目标列表必须至少与源列表一样长。如果更长,则目标列表中的其余元素不受影响。

这使用术语“长”和“更长”,它们似乎是指列表的长度。但是,如果您查看List 规范,则没有 length 的定义——但是,sizeArrayList 中的 都有定义>容量。不清楚Collections.copy() 是根据列表的size 还是capacity 定义的。

事实上,Collections.copy() 应该根据列表的大小 来定义。 Collections.copy()JDK 9 specification 已修改为:

目标列表的大小必须大于或等于源列表的大小。如果它更大,则目标列表中的其余元素不受影响。

ArrayList 构造函数的参数设置它的初始容量,而不是它的大小size 是列表中当前存在的元素的数量。 Collections.copy() 要求目标列表的大小大于或等于源的大小。这就是为什么当您尝试将大小为 1 的列表复制到大小为 0 的列表时得到IndexOutOfBoundsException 的原因。

【讨论】:

    【解决方案2】:

    如果目标List太小,我会抛出IndexOutOfBoundsException。

    import java.util.*;
    
    public class CollectionsDemo {
       public static void main(String args[]) {
          // create two lists    
          List<String> srclst = new ArrayList<String>(5);
          List<String> destlst = new ArrayList<String>(10);
    
          // populate two lists
          srclst.add("a");
          srclst.add("b");
          srclst.add("d");
    
          destlst.add("e");
          destlst.add("f");
          destlst.add("g");
    
    
          // copy into dest list
          Collections.copy(destlst, srclst);            
    
          System.out.println("Value of source list: "+srclst);
          System.out.println("Value of destination list: "+destlst);
       }    
    

    【讨论】:

      【解决方案3】:

      我觉得你对initialCapacity的理解有问题。就像初始化一个填充了“null”值的基元数组一样,在ArrayList 的初始化过程中不会发生任何事情。通过设置initialCapacityn,您所做的就是让编译器知道它应该为n 对象分配足够的内存。请注意,即使分配了内存,列表的大小仍然为 0,因为其中没有任何元素。

      关于copy()。您正在尝试将lista 复制到初始容量为lista.size() = 0 的列表中。那里似乎没有什么不对劲?

      这种混淆可能是因为文档根本没有提到size 这个词,但这是这里的关键因素。

      【讨论】:

        猜你喜欢
        • 2013-06-22
        • 2012-08-08
        • 2023-01-25
        • 1970-01-01
        • 1970-01-01
        • 2013-03-04
        • 1970-01-01
        相关资源
        最近更新 更多