ArrayList 的初始大小

352

您可以通过以下方式设置ArrayList的初始大小

ArrayList<Integer> arr=new ArrayList<Integer>(10);

然而,你不能这样做

arr.add(5, 10);

因为这会导致数组越界异常。

如果你无法访问所分配的空间,那么设置初始大小有什么用呢?

add函数被定义为add(int index, Object element),所以我没有将元素添加到索引10。


57
从文档中并不明显,一个列表需要添加至少 n 个项才能在第 n-1 项之前进行 set/add 操作。 - Perception
6
感知:我不知道是否显而易见,但是它已经被明确说明了。必须仔细阅读Java文档。 Throws: IndexOutOfBoundsException - 如果索引超出范围(index < 0 || index >= size())。 - Natix
3
构造函数的意思是“构造一个具有指定初始容量的空列表。”由于这是一个空列表,因此不能有索引5。但我同意一开始可能看不出来这一点... - quaylar
20
我认为这样说也是公平的:如果你将一个数组初始化为特定值,那么你会假设比该值更低的索引是可用的,而这就是 ArrayList。我个人希望有一种方法可以设置尺寸,这样我就可以在特定的索引位置放置东西。然而,这种方法似乎明显缺少。 - Andrew Wyld
4
这个设计集合的蠢货是谁啊?这样做会迫使并行实例化一个包含可变长度元素的结构(即ArrayList<String[]>,其中每个数组的长度可以不同)时需要进行冗余工作。如果内存已经分配好了,那么在添加N个元素后,这些索引应该从一开始就可以直接访问。难道Oracle没有从C/C++、C#、Objective C和Swift中学到这种模式吗?! - patrickjp93
1
@AndrewWyld java.util.Vector有一个setSize(int)方法,但是该类不幸地被同步化了,因此比ArrayList慢。 - neuralmer
18个回答

503

你误解了数组列表的大小和容量:

  • 大小 是列表中元素的数量;
  • 容量 是列表可以潜在地容纳多少元素,而不必重新分配其内部结构。

当你调用 new ArrayList<Integer>(10) 时,你设置的是列表的初始容量,而不是它的大小。换句话说,以这种方式构建的数组列表在创建时为空。

向数组列表添加十个元素的一种方法是使用循环:

for (int i = 0; i < 10; i++) {
  arr.add(0);
}

完成此操作后,您现在可以修改索引为0..9的元素。


66
一个更短的循环是while(arr.size() < 10) arr.add(0);它可以用来表达数组大小至少需要为10,这样你就可以使用arr.set(9, n); - Peter Lawrey
12
+1:回答太棒了,如果我可以的话我会给+10。从API中并不立即清楚为什么您不能在单个构造函数调用中同时设置初始大小和初始容量。您需要阅读API并说:“哦,我想ArrayList没有方法或构造函数来完成这个操作”。 - demongolem
1
@PeterLawrey,你的代码可能更短,但每次循环迭代包含两个方法调用,而不是一个。 - neuralmer
1
@neuralmer 我预期 size() 和 add() 会被内联,因此在运行时不会发生实际的方法调用。 - Peter Lawrey

161

如果您想要一个预定义大小的列表,也可以使用以下方式:

List<Integer> arr = Arrays.asList(new Integer[10]);

17
这里有一个小缺点,生成的列表里全是空值。使用Guava,我们可以用Ints.asList(new int[10])来初始化列表并填充0。不过这个模式很干净,感谢提供示例。 - dimo414
1
这个问题涉及到ArrayList<E>,而你使用的是List<E>。难道没有人注意到这一点吗???而且他们还点赞了这个无关紧要的答案!我不会给你的回答投反对票,因为我从来不这样做。简直是……我的天啊! - Apostolos
5
ArrayListList 接口的一种实现,而 Arrays.asList 返回的是一个 ArrayList。我建议你查找一下多态性。 - Liam Potter
1
这将返回一个固定大小的列表。尝试添加更多元素会抛出“UnsupportedOperationException”异常。 - Koray Tugay
2
@dimo414,有时候使用null比添加第三方库依赖更好。 - Rohit Gaikwad
显示剩余3条评论

69

如果你想使用Collections.fill(list, obj); 方法来用重复的对象填充列表,可以尝试以下方式:

ArrayList<Integer> arr=new ArrayList<Integer>(Collections.nCopies(10, 0));

该行代码将10次将0复制到您的ArrayList中。


24

容量(Capacity)指的是 ArrayList 内部用于存储元素的底层数组的长度,与其 大小(size) 不同。 大小等于包含在 ArrayList(以及任何其他 List 实现中)中的元素数量。

容量始终大于或等于列表的 大小,它只是用于实现 ArrayList 的底层数组的长度。

调用列表的 set(index, element) 方法时,index 是实际的列表元素数量(即 大小),而不是数组长度(即 容量,这是 ArrayList 特定的实现细节),因此在代码中使用了零索引而引发了 AIOOBE 异常。

set 方法是所有 List 实现都共有的方法,例如 LinkedList 并未像 ArrayList 一样使用数组实现,而是通过链接条目形成链表实现的。

编辑说明:实际上您使用的是 add(index, element) 方法,而非 set(index, element),但其原理在本例中是相同的。


10
如果您想按索引添加元素,则可以使用数组。
    String [] test = new String[length];
    test[0] = "add";

8
OP 最初想使用列表而不是数组。 - Stephan

9

10是AL的初始容量,而不是它的大小(大小为0)。如果您要添加很多元素,应该将初始容量设置为较高值,以避免在继续添加元素时扩展容量的开销。


7
这可能对某些人有帮助 -
ArrayList<Integer> integerArrayList = new ArrayList<>(Arrays.asList(new Integer[10]));

6

虽然有点晚了,但在Java 8之后,我个人发现使用Stream API的以下方法更为简洁,并可作为被接受的答案的替代方案。

例如:

Arrays.stream(new int[size]).boxed().collect(Collectors.toList())

其中size表示所需List的大小,且所有List中的元素都会初始化为0,不会出现这里提到的缺点

(我进行了快速搜索并未在任何已发布的答案中看到stream - 如果此答案是多余的,请告诉我,我可以将其删除)


我也可以在运行时填充它吗?我有一个从数据库中获取的对象列表,我需要将该列表的大小固定。 - Kirill

6

我想对你的问题给出一个确切的答案:

在ArrayList上设置初始大小可以减少内部内存重新分配的次数。 该列表由数组支持。如果您指定了初始容量为0,那么在插入第一个元素时,内部数组就必须被重新调整大小。 如果您大概知道列表将保存多少个元素,那么设置初始容量将减少您使用列表时发生的内存重新分配次数。


4

我遇到了类似的问题,只知道ArrayList是List接口的可调整大小的数组实现,我也希望您可以在任何位置添加元素,但至少有定义初始大小的选项。

无论如何,您可以首先创建一个数组,然后将其转换为列表:

  int index = 5;
  int size = 10;

  Integer[] array = new Integer[size];
  array[index] = value;
  ...
  List<Integer> list = Arrays.asList(array);

或者

  List<Integer> list = Arrays.asList(new Integer[size]);
  list.set(index, value);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接