Java学习day031 泛型数组列表(访问数组列表元素、类型化与原始数组列表的兼容性)

使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。

day031   泛型数组列表(访问数组列表元素、类型化与原始数组列表的兼容性)

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

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

int actualSize = ...;
Employee[] staff = new Employee[actualSiza];

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

ArrayList是一个采用类型参数(type parameter)的泛型类(generic class)。为了指定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来加在后面,例如,ArrayList<Employee>。

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

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

两边都使用类型参数Employee,这有些繁琐。可以省去右边的类型参数:

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

这被称为“菱形”语法,因为空尖括号<>就像是一个菱形。可以结合new操作符使用菱形语法。编译器会检查新值是什么。如果赋值给一个变量,或传递到某个方法,或者从某个方法返回,编译器会检査这个变量、参数或方法的泛型类型,然后将这个类型放在<>中。在这个例子中,new ArrayList<>()将赋至一个类型为ArrayList的变量,所以泛型类型为Employee。

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

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

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

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

staff.ensureCapacity(lOO);

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

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

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

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

staff.size()

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

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

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

     

 


1.访问数组列表元素

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

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

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

staff.set(i,harry);

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

a[i]=harry;

使用add方法为数组添加新元素,而不要使用set方法,它只能替换数组中已经存在的元素内容。

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

Employee e = staff.get(i);

等价于:

Employee e = a[i];

下面这个技巧可以一举两得,既可以灵活地扩展数组,又可以方便地访问数组元素。首先,创建一个数组,并添加所有的元素。

ArrayList<X> list = new ArrayList<>();
while (...)
{
    x=...;
    list.add(x);
}

执行完上述操作后,使用toArray方法将数组元素拷贝到一个数组中。

X[] a = new X[list.size()];
list.toArray(a);

除了在数组列表的尾部追加元素之外,还可以在数组列表的中间插入元素,使用带索引参数的add方法。

int n=staff.size()/2;
staff.add(n,e);

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

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

Employee e=staff.remove(n);

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

对数组实施插人和删除元素的操作其效率比较低。对于小型数组来说,这一点不必担心。但如果数组存储的元素数比较多,又经常需要在中间位置插入、删除元素,就应该考虑使用链表了。

可以使用“for each“循环遍历数组列表:

for (Eployee e : staff)
    do something with e

这个循环和下列代码具有相同的效果

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

下面的程序是对之前EmployeeTest做修改之后的程序。在这里,将Employee[]数组替换成了ArrayList。请注意下面的变化:

•不必指出数组的大小。

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

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

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

/**
 *@author zzhao
 */

import java.time.*;

public class ArrayListTest 
{
	public static void main(String[] args)
	{
		//fill the staff array with three Employee objects
		ArrayList<Employee> staff = new ArrayList<>();
		
		staff.add(new Employee("Carl Cracker",75000,1987,12,15));
		staff.add(new Employee("Harry Hacker",50000,1989,10,1));
		staff.add(new Employee("Tony Tester",40000,1990,3,15));
		
		//raise everyone's salary by 5%
		for (Employee e : staff)
			e.raiseSalary(5);
		
		//print out information about all Employee objects
		for (Employee e : staff)
        System.out.println("name="+e.getName()+",salary="+e.getSalary()+",hireDay="+e.getHireDay());
	}
}

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

在你自己的代码中,你可能更愿意使用类型参数来增加安全性。下面,你会了解如何与没有使用类型参数的遗留代码交互操作。假设有下面这个遗留下来的类:

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

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

ArrayList<Employee> staff = ...;
employeeDB.update(staff);

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

使用类型转换并不能避免出现警告。

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

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

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

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

一旦能确保不会造成严重的后果,可以用@SuppressWamings("unchecked")标注来标记这个变量能够接受类型转换,如下所示:

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值