专业的JAVA编程教程与资源

网站首页 > java教程 正文

Java核心技术:泛型数组列表+访问数组列表+类型化与原始数组列表

temp10 2024-10-05 01:03:42 java教程 13 ℃ 0 评论

泛型数组列表

在许多程序设计语言中,特别是在C语言中,必须在编译的时候就确定整个数组的大小。程序员对此十分反感,因为这样做将迫使程序员做出一些不情愿的折中。例如,在一个部门中有多少雇员?肯定不会超过100人。一旦出现一个拥有150名雇员的大型部门呢?你愿意为那些仅有10名雇员的部门浪费90名雇员占据的存储空间吗?

在Java中,情况就好多了。它允许在运行时确定数组的大小。

Java核心技术:泛型数组列表+访问数组列表+类型化与原始数组列表

int actualSize = . . . ;

Employee[ ] staff = new Employee[actualSize];

当然,这段代码并没有完全解决运行时动态更改数组的问题。一旦确定了数组的大小,改变它就不太容易了。在Java中,解决这个问题最简单的方法是使用Java中另外一个被称为ArrayList的类。它使用起来有点像数组,但在添加或删除元素时,具有自动调节数组容量的功能,而不需要为此编写任何代码。

在JDK 5.0中,ArrayList是一个采用类型参数(type parameter)的泛型类(generic class)。为了指定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来加在后面,例如,ArrayList <Employee>。在第13章中将可以看到如何自己定义一个泛型类,在此并不需要了解任何技术细节就可以使用ArrayList类型。

下面声明和构造一个保存Employee对象的数组列表:

ArrayList<Employee> staff = new ArrayList<Employee>( );

使用add方法可以将元素添加到数组列表中。例如,下面展示了如何将雇员对象添加到数组列表中的方法:

staff.add(new Employee("Harry Hacker", . . .));

staff.add(new Employee("Tony Tester", . . .));

数组列表管理着对象引用的一个内部数组。最终,数组的全部空间有可能被用尽。这就显现出数组列表的操作魅力:如果调用add且内部数组已经满了,数组列表就将自动地创建一个更大的数组,并将所有的对象从较小的数组中拷贝到较大的数组中。

如果已经清楚或能够估计出数组可能存储的元素数量,就可以在填充数组之前调用

ensureCapacity方法:

staff.ensureCapacity(100);

这个方法调用将分配一个包含100个对象的内部数组。然后调用100次add,而不用重新分配空间。

另外,还可以把初始容量传递给ArrayList构造器:

ArrayList<Employee> staff = new ArrayList<Employee>(100);

警告:分配数组列表,如下所示:

new ArrayList<Employee>(100) //capacity is 100

它与为新数组分配空间有所不同:

new Employee[100] //size is 100

数组列表的容量与数组的大小有一个非常重要的区别。如果为数组分配100个元素的存储空间,那么数组就有100个空位置可以使用。而容量为100个元素的数组列表只是拥有保存100个元素的潜力(实际上,重新分配空间的话,将会超过100),但是在最初,甚至完成初始化构造之后,数组列表根本就不含有任何元素。

size方法将返回数组列表中包含的实际元素数目。例如,

staff.size( )

将返回staff数组列表的当前元素数量,它等价于数组a的a.length。

一旦能够确认数组列表的大小不再发生变化,就可以调用trimToSize方法。这个方法将存储区域的大小调整为当前元素数量所需要的存储空间数目。垃圾回收器将回收多余的存储空间。

一旦整理了数组列表的大小,添加新元素就需要花时间再次移动存储块,所以应该在确认不会添加任何元素时,再调用trimToSize。

java.util.ArrayList<T> 1.2

? ArrayList<T>( )

构造一个空数组列表。

? ArrayList<T>(int initialCapacity)

用指定容量构造一个空数组列表。

参数:initialCapacity

数组列表的最初容量

? boolean add(T obj)

在数组列表的尾端添加一个元素。永远返回true。

参数:obj

添加的元素

? int size( )

返回存储在数组列表中的当前元素数量。(这个值将小于或等于数组列表的容量。)

? void ensureCapacity(int capacity)

确保数组列表在不重新分配内部存储空间的情况下就能够保存给定数量的元素。

参数:capacity

需要的存储容量

? void trimToSize( )

将数组列表的存储容量削减为当前尺寸。

访问数组列表元素

很遗憾,天下没有免费的午餐。数组列表自动扩展容量的便利增加了访问元素语法的复杂程度。其原因是ArrayList类并不是Java程序设计语言的一部分;它只是一个由某些人编写且被放置在标准库中的一个实用类。

使用get和set方法实现访问或改变数组元素的操作,而不使用人们喜爱的 [ ] 语法格式。

例如,要设置第i个元素,可以使用:

staff.set(i, harry);

它等价于对数组a的元素赋值:(数组的下标从0开始。)

a[i] = harry;

使用下列格式获得数组列表的元素:

Employee e = staff.get(i);

等价于:

Employee e = a[i];

在JDK 5.0中,可以使用“每一个”对数组列表循环:

for (Employee e:staff)

//do something with e

用传统的循环结构可以写成

for (int i = 0;i<staff.size( );i++)
{
Employee e = (Employee) staff.get(i);
do something with e
}

除了在数组列表的尾部追加元素之外,还可以在数组列表的中间插入元素。

int n = staff.size( )/2;

staff.add(n, e);

为了插入一个新元素,位于n之后的所有元素都要向后移动一个位置。如果插入新元素后,数组列表的大小超过了容量,数组列表就会被重新分配存储空间。

同样地,可以从数组列表中删除一个元素。

Employee e = staff.remove(n);

位于这个位置之后的所有元素都向前移动一个位置,并且数组的大小减1。

对数组实施插入和删除元素的操作,效率都比较低。这一点对于小型数组来说不必担心。

但如果数组存储的元素数比较多,又经常需要在中间位置插入、删除元素,就应该考虑使用链表了。有关链表操作的实现方式将在卷II中讲述。

例5-4是对第4章中EmployeeTest做出修改后的程序。在这里,将Employee[ ]数组替换成了ArrayList<Employee>。请注意下面的变化:

? 不必指出数组的大小。

? 使用add将任意多的元素添加到数组中。

? 使用size( )替代length计算元素的数目。

? 使用a.get(i)替代a[i]访问元素。

例5-4 ArrayListTest.java

java.util.ArrayList<T> 1.2

? void set(int index,T obj)

设置数组列表指定位置的元素值,这个操作将覆写这个位置的原有内容。

参数:index 位置(必须介于0~size( )-1之间)

obj 新的值

? T get(int index)

获取指定位置的元素值。

参数:index 获取的元素位置(必须介于0~size( )-1之间)

? void add(int index,T obj)

向后移动元素,以便插入元素。

参数:index 插入位置(必须介于0~size( )之间)

obj 新元素

? T remove(int index)

删除一个元素,并将后面的元素向前移动。被删除的元素由返回值返回。

参数:index 被删除的元素位置(必须介于0~size( )-1之间)

类型化与原始数组列表的兼容性

在采用JDK 5.0以后的版本编写程序代码时,应该使用类型参数的数组列表,例如,ArrayList<Employee>。然而,有可能会发生与现存程序中的原始ArrayList类型进行交叉操作的情形。

假设有下面这个遗留下来的类:

public class EmployeeDB
{
public void update(ArrayList list) { . . . }
public ArrayList find(String query) { . . . }
}

我们可以将一个类型化的数组列表传递给update方法,而并不需要进行任何类型转换。

ArrayList<Employee> staff = . . .;

employeeDB.update(staff);

也可以将staff对象传递给update方法。

警告:尽管编译器没有给出任何错误信息或警告,但是这样调用并不太安全。在update方法中,添加到数组列表中的元素可能不是Employee类型。在对这些元素进行检索时就会出现异常。听起来似乎很吓人,但思考一下就会发现,这与JDK 5.0以前的版本是一样的。虚拟机的完整性绝对没有受到威胁。在这种情形下,既没有削减安全性,也没有受益于编译时的检查。

相反地,将一个原始ArrayList赋给一个类型化ArrayList会得到一个警告。

ArrayList<Employee> result = employeeDB.find(query); //yields warning

注意:为了能够看到警告错误的文字信息,要将编译选项置为-Xlint:unchecked。

使用类型转换将可以避免出现警告。

ArrayList<Employee> result = (ArrayList<Employee>) employeeDB.find(query); //yields another warning

这样,将会得到另外一个警告信息,告诉你类型转换有误。

这就是Java中不尽人意的参数化类型的限制所带来的结果。鉴于兼容性的考虑,编译器在对类型转换进行检查之后,如果没有发现违反规则的现象,就将所有的类型化数组列表转换成原始ArrayList对象。在程序运行时,所有的数组列表都是一样的,即没有虚拟机中的类型参数。因此,类型转换(ArrayList)和(ArrayList<Employee>)将执行相同的运行时检查。

在这种情形下,不必做什么。只要在与遗留的代码进行交叉操作时,研究一下编译器的警告性提示,并确保这些警告不会造成太严重的后果就行了。

觉得文章不错的话,可以转发关注小编,之后持续更新干货文章!!!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表