在 C++ 语言中,我们必须在编译时就确定整个数组的大小。这是真真地让人厌烦。但在 Java 中,情况就好一些了。它允许在运行时确定数组的大小:
int actualSize = ...;
Employee[] staff = new Employee[actualsize];
之所以说它是“好一些”,是因为这样做并没有完全解决运行时动态更改数组的问题。
一旦确定了数组的大小,再想改变它就不容易了。在 Java 中,解决这个问题最简单的方法是使用 Java 中的一个被称为 ArrayList 的类。
ArraysList 是一个采用 类型参数(type parameter)的 泛型类(generic class)。为了指定数组列表保存的元素对象类型,需要一对尖括号将类名括起来加在后面。
下面声明和构造一个保存 Employee 对象的数组列表:
ArrayList<Employee> staff = new ArrayList<>();
这被称为“菱形”语法。
注释: 在 Java 的老版本中,程序员使用 Vector 类实现动态数组。不过,ArrayList 类更加有效,没有任何理由一定要使用 Vector 类。
我们使用 add 方法可以将元素添加到数组列表中。例如:
staff.add(new Employee("Harry Hacker",...);
staff.add(new Employee("Tony Tesker",...);
这就是数组列表的魅力所在:如果调用 add 且内部数组已经满了,数组列表就将自动地创建一个更大的数组,并将所有的对象从较小的数组中拷贝到更大的数组中去。
如果已经清楚或能够估计出数组可能存储的元素数量,就可以在填充数组之前调用 ensureCapacity 方法:
staff.ensureCapacity(100);
这个方法调用将分配给一个包括 100 个对象的内部数组。然后调用 100 次 add,而不用重新分配空间。
另外,还可以把初始容量传递给 ArrayList 构造器:
ArrayList<Employee> staff = new ArrayList<>();
警告: 分配数组列表,如下所示:
new ArrayList<>(100);
它与为新数组分配空间有所不同:
new Employee[100];
数组列表的容量y与数组的大小有一个非常重要的区别。如果为数组分配 100 个元素的存储空间,数组就有 100 个空位置可以使用。而容量 100 的数组列表只是拥有保存 100 个元素的潜力(实际上,重新分配空间的话,将会超过 100),但是在最初,甚至在完成初始化构造之后,数组列表根本就不含有任何元素。
size 方法将返回数组列表中包含的世纪元素数目。例如:
staff.size()
将返回 staff 数组列表的当前元素数目。它等价于数组 a 的 a.length。
一旦整理了数组列表的大小,添加新元素就需要花时间再次移动存储块,所以应该在确认不会添加1任何元素之后,再调用 trimToSize。
很遗憾,天下没有免费的午餐。数组列表自动扩展容量的便利增加了访问元素语法的复杂程度。其原因是 ArrayList 类并不是 Java 程序设计语言的一部分,它只是一个由某些人编写且放在标准库中的一个实用类。
使用 get 和 set 方法实现访问或改变数组元素的操作,而不是使用人们喜爱的 [ ] 的语法格式。
例如,要设置第 i 个元素,可以使用:
staff.set(i, harry);
它等价于对数组 a 的元素赋值(数组的下标从 0 开始):
a[i] = harry;
警告: set 方法只能替换数组中已经存在的元素内容。
下面这个技巧可以一举两得,既可以灵活地扩展数组,又可以方便的访问数组元素。
先创建一个数组,并添加所有的元素:
ArrayList<> list = new ArrayList<>();
while(...){
x = ...;
list.add(x);
}
然后使用 toArray 方法将数组元素拷贝到一个数组中:
x[] a = new X[list.size()];
list.toArray(a);
这样就好多了。