11.1 泛型和类型安全的容器
该部分主要是针对一个基本的容器类问题,就是当你不使用泛型(不定义类型参数)时,你无法保证加入到容器里的对象都是同一类型的对象,这样在数据读取的时候很容易就会发生类型错误。
举例:假设有一个Apple类和Orange类,当代码像下面一样编写时,编译器是不会报告错误,可是当你取数据的时候,你根本不知道取出来的对象到底是Apple还是Orange,很可能就会发生类型转换异常,运行时就会报告: java.lang.ClassCastException: Orange1 cannot be cast to Apple1
ArrayList arraylist = new ArrayList();
arraylist.add(new Apple());
arraylist.add(new Orange());
Apple1 apple = (Apple1)arraylist.get(0);
Apple1 apple1 = (Apple1)arraylist.get(1);
针对上面的问题,可以使用泛型,指定容器能够容纳的对象类型,从而在编译期就能够阻止以上问题的发生
ArrayList<Apple1> arraylist = new ArrayList<Apple1>(); //泛型
arraylist.add(new Apple1());
//arraylist.add(new Orange1()); //该段代码就会在编译期时检测到错误
Apple1 apple = arraylist.get(0); //因为容器已经确定保存的对象类型,因此这个地方的类型强制转换都不需要了。
课后习题(Exercise 1):
class Gerbil {
private final int gerbilNumber;
Gerbil(int gerbilNumber) {
this.gerbilNumber = gerbilNumber;
}
public String toString() {
return "gerbil " + gerbilNumber;
}
public void hop() {
System.out.println(this + " is hopping");
}
}
public class E01_Gerbil {
public static void main(String args[]) {
ArrayList<Gerbil> gerbils = new ArrayList<Gerbil>();
for(int i = 0; i < 10; i++)
gerbils.add(new Gerbil(i));
for(int i = 0; i < gerbils.size(); i++)
gerbils.get(i).hop();
}
}
11.2 基本概念
无
11.3
无
11.4 容器的打印
public class Demo11_4 {
public static Collection<String> fill(Collection<String> collection) {
collection.add("One");
collection.add("Two");
collection.add("Three");
collection.add("Three");
return collection;
}
public static Map<String,String> fill(Map<String,String> map) {
map.put("One","1");
map.put("Two","2");
map.put("Three","3");
return map;
}
public static void main(String[] args) {
System.out.println(fill(new ArrayList<String>()));
System.out.println(fill(new LinkedList<String>()));
System.out.println(fill(new HashSet<String>()));
System.out.println(fill(new TreeSet<String>()));
System.out.println(fill(new LinkedHashSet<String>()));
System.out.println(fill(new HashMap<String,String>()));
System.out.println(fill(new TreeMap<String,String>()));
System.out.println(fill(new LinkedHashMap<String,String>()));
}
}
输出:
[One, Two, Three, Three]
[One, Two, Three, Three]
[Three, One, Two]
[One, Three, Two]
[One, Two, Three]
{Three=3, One=1, Two=2}
{One=1, Three=3, Two=2}
{One=1, Two=2, Three=3}
2个注意点:
1) 因为Set不允许有相同的值,当加入相同的值时,会覆盖,因此HashSet和TreeSet输出的值只有3个
2)TreeSet输出的值是 [One, Three, Two] ,因为TreeSet是按照比较结果的升序保存对象,对不同的对象,有不同的比较(以上程序是String类型)
- BigDecimal、BigIneger以及所有数值型对应包装类:按它们对应的数值的大小进行比较。
- Character:按字符的UNICODE值进行比较。
- Boolean:true对应的包装类实例大于false对应的包装类实例。
- String:按字符串中字符的UNICODE值进行比较。
- Date、Time:后面的时间、日期比前面的时间、日期大。
11.5 List
List的一些常用方法,详细见API,范例中的Pets可以从以下地址下载(非本人发布,借用fisherwan发布的资源)
http://download.youkuaiyun.com/download/u011248694/7537217
11.6 迭代器
迭代器(Iterator)
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素
范例:SimpleIteration就介绍了简单的迭代器用法,看下CrossContainerIteration
public class CrossContainerIteration {
public static void display(Iterator<Pet> it) {
while(it.hasNext()) {
Pet p = it.next();
System.out.print(p.id() + ":" + p + " ");
}
System.out.println();
}
public static void main(String[] args) {
ArrayList<Pet> pets = Pets.arrayList(8);
LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
HashSet<Pet> petsHS = new HashSet<Pet>(pets);
TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);
display(pets.iterator());
display(petsLL.iterator());
display(petsHS.iterator());
display(petsTS.iterator());
}
}
display的方法,可以忽略传入的容器对象,而根本不用考虑说是ArrayList的对象,还是LinkedList等其他。个人感觉有点像多态的处理方式。
display()方法在这边是通过传入Iterator对象来实现忽略容器类型,实际上针对这个范例,还可以通过容器基类接口Collection来实现,因为Iterator本身就是对 collection 进行迭代的迭代器:
<pre name="code" class="java">public static void display(Collection pets)
{
Iterator<Pet> it = pets.iterator();
...............
}
public static void main(String[] args) {
ArrayList<Pet> pets = Pets.arrayList(8);
LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
HashSet<Pet> petsHS = new HashSet<Pet>(pets);
TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);
display(pets); //ArrayList
display(petsLL); //LinkedList
display(petsHS); //HashSet
display(petsTS); //TreeSet
</pre>注意点:<pre name="code" class="java">LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
HashSet<Pet> petsHS = new HashSet<Pet>(pets);
TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);
以上几个容器之间是可以互相转换的,但是不能直接通过类型强制转换完成
11.6.1 Listinterator
Listiterator是iterator的子类,比iterator多了向前访问的功能,以及一些其他的,可查询API,Listinterator 由于涉及到前后移动,在游标cursor上很容易就犯错了
自己在学习的时候写了以下代码片,结果对输出很不理解,绕晕了:
public static void main(String[] args) {
ArrayList<Pet> pets = Pets.arrayList(8);
System.out.println("pets = "+pets);
ListIterator<Pet> it = pets.listIterator();
while(it.hasNext()) {
Pet pet = it.next();
<span style="white-space:pre"> </span>System.out.println(pet.id()+ " "+ pet);
if(pet.id() == 2)
{
pet = it.previous();
System.out.println("previous = "+ pet.id()+ " "+ pet);
pet = it.next();
System.out.println("next = "+ pet.id()+ " "+ pet);
}
}
}
输出
pets = [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx]
0 Rat
1 Manx
2 Cymric
previous = 2 Cymric
next = 2 Cymric
3 Mutt
4 Pug
5 Cymric
6 Pug
7 Manx
在previous和next处竟然都输输出Cymric。
经过调试发现,原来是next()方法和previous()的方法在游标cursor的移动上有区别。
说明:当访问到 it.next(); 时,是先把当前位置给对象,然后游标cursor再往前移动,而 访问到 it.previous();时,是游标cursor先往前移动,再把位置给对象。
也就是说,在即将进入到 if 语句的时候,此时pet对象指向的位置是2,而cursor=3,此时执行previous()的时候,cursor=2,然后再把位置给pet对象,也就是说pet.id() =2
,此时又接着执行了next(),执行的时候,cursor先把2赋给pet.id(),然后cursor=3; 执行完后跳出if语句,又进入到while循环里,此时又执行了一次next动作,cursor又同样的,先把当前值3给pet.id,所以pet取的值就是3 Mutt。
可以看下ArrayList.class里的代码
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
<pre name="code" class="java"> public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}