第 8章 集合与泛型
8.1 集合简介
8.2 迭代器
8.3 for-each循环
8.4 泛型定义
8.5 泛型的使用
8.6 Java泛型的局限性第 8章 集合与泛型(续)
8.7 Autoboxing及 Unboxing
8.8 Collection
8.9 List及其实现类
8.10 Collections类
8.11 习题
8.1 集合简介
集合对象会自动扩展,以容纳添加到其中的所有对象。
集合中只能容纳对象。
Java 2的集合类型被统一组织在 Java集合架构( Java Collections Framework)中。
8.1.1 集合架构
Collection
List Set
Map
SortedSet SortedMap
ArrayList LinkedList HashSet TreeSet HashMap TreeMap
AbstractList AbstractSet AbstractMap
AbstractSequentialList
AbstractCollection
8.1.1 集合架构 (续)
Collection:
– 每个元素都是单一对象。
List以特定顺序容纳元素。
Set中不能有重复的元素。
Map:
– 每个元素都是一对 key-value(键值/实值)
对象,且每个元素中的键值都不能与其他元素中的键值相同。
8.1 集合简介(续)
接口:
–Collection,List,Set
–Map
具体实现类:
–ArrayList,LinkedList,HashSet、
TreeSet
–HashMap,TreeMap
算法:
–java.util.Collections
8.1.1 集合架构 (续)
核心接口是 Java集合架构的基础,所有的具体集合类都是它们的实现。理想情况下,程序大多时候只会和这些接口打交道,而只有在创建集合时才会指定集合的具体类型。
例 8-1 集合源代码 编译运行
8.1.2 泛型集合的引入
JDK 1.4.2和更早版本的集合有一个共同的缺点:一旦将某个对象添加到其中,
该对象便失去了其原有的类型信息,集合中所容纳的元素其实只是一些指向
Object对象的引用。
8.1.2 泛型集合的引入
JDK 1.4.2和更早版本的集合:
– 集合对其所容纳的对象类型没有任何限制,
可以将任意类型的对象添加到同一集合中。
– 从集合中取出对象时必须将其强制转换成正确的类型。
例 8-2 JDK 1.4.2集合的缺点源代码 编译运行
8.1.2 泛型集合的引入(续)
JDK 1.5引入了泛型( Generics):
– 从泛型集合中取出元素时不再需要进行强制类型转换。
– 编译器会保证添加到集合中的元素一定是参数化类型指定的类型。
例 8-3 使用泛型集合源代码 编译运行
8.2 迭代器
JDK 1.4.2中,
public interface Iterator {
boolean hasNext();
Object next();
void remove();
}
例 8-4 迭代器源代码 运 行
8.2 迭代器(续)
Enumeration(枚举器):
–boolean hasMoreElements()
–Object nextElement()
8.3 for-each循环
static void printAll(Collection c)
{
for (Object e:c )
System.out.println(e);
}
例 8-5 使用 for-each循环访问集合元素源代码 运 行
8.3 for-each循环(续)
for-each循环也适用于数组。
例 8-6 使用 for-each循环访问数组元素源代码 运 行
8.4 泛型定义
8.4.1 泛型接口和类的定义
8.4.2 泛型方法的定义
8.4.1 泛型接口和类的定义
泛型接口(或类)就是指带有参数化类型的接口(或类):
public interface Iterator<E>
{
E next();
boolean hasNext();
void remove();
}
8.4.1 泛型接口和类的定义(续)
参数化类型可以被沿用作父接口(或父类)
的参数化类型:
public interface Iterable<T>
{
Iterator<T> iterator();
}
public interface Collection<E> extends
Iterable<E>
{
boolean add(E o) ;
......
}
8.4.1 泛型接口和类的定义(续)
泛型接口(或类)中可以带有多个参数化类型:
public interface Map<K,V>
{
V get(Object key);
......
}
public class HashMap<K,V> extends
AbstractMap<K,V> implements
Map<K,V>,Cloneable,Serialize
{
......
}
8.4.1 泛型接口和类的定义(续)
定义泛型接口或类时,其中的参数化类型可以带有限制条件:
class MyList<E extends Number>
{
......
}
8.4.2 泛型方法的定义
泛型方法是指带有参数化类型的方法:
public static<T> void fromArrayToCollection(
T[] a,Collection<T> c)
{
for(T o:a)
{
c.add(o);
}
}
8.4.2 泛型方法的定义(续)
public class ArrayList<E> extends AbstractList<E>
implements List<E>,RandomAccess,
Cloneable,
Serializable
{
public <T> T[] toArray(T[] a)
{
......
}
......
}
8.4.2 泛型方法的定义(续)
定义泛型方法时,参数化类型列表中出现的类型可以带有限制条件:
public static<T extends Number> void
fromArrayToCollection(T[] a,Collection<T> c)
{
for(T o:a)
{
c.add(o);
}
}
8.5 泛型的使用
8.5.1 泛型类和接口的使用
8.5.2 泛型方法的使用
8.5.1 泛型类和接口的使用
Collection<Integer> c1=new
ArrayList<Integer>();
Collection<String> c2=new ArrayList<String>();
c1.add(new Integer(1));
c2.add(new String("1"));
c1.add(new String(“2”)); //错误
c2.add(new Object()); //错误
c2.add(new Integer(1)); //错误例 8-7 使用泛型类源代码 运 行
8.5.1 泛型类和接口的使用(续)
错误:
List<Integer> li=new ArrayList<Integer>();
List<String> ls=li; //编译错误
List<Integer> li=new ArrayList<Integer>();
List<Object> lo=li; //编译错误注意,如果 A是 B的子类(或子接口),G
是一个泛型类(或接口),G<A>并不是
G<B>的子类型。
8.5.1 泛型类和接口的使用(续)
import java.util.*;
public class ErrorUseGenerics
{
static void printAll(Collection<Object> c)
{
for (Object e:c )
System.out.println(e);
}
public static void main(String[] args)
{
ArrayList<Integer> a=new ArrayList<Integer>();
a.add(new Integer(1));
a.add(new Integer(2));
a.add(new Integer(1));
HashSet<String> h=new HashSet<String>();
h.add("String 1");
h.add("String 2");
h.add("String 3");
printAll(a); //编译错误
printAll(h); //编译错误
}
}
8.5.1 泛型类和接口的使用(续)
上述程序中的方法 printAll()可以改写为:
static<T> void printAll(Collection<T> c)
{
for (Object e:c )
System.out.println(e);
}
更好的改写:
static void printAll(Collection<?> c)
{
for (Object e:c )
System.out.println(e);
}
8.5.1 泛型类和接口的使用(续)
Collection<?> c=new ArrayList<Integer>();
c.add(new Integer(1)); //编译错误
c.add(new Object()); //编译错误
ArrayList<Integer> a=new ArrayList<Integer>();
a.add(new Integer(1));
List<?> li=a;
Object o=li.get(0);
8.5.1 泛型类和接口的使用(续)
使用符号,?,时,可以指定限制条件:
static void printAll(Collection<? extends Number> c)
{
for (Object e:c )
System.out.println(e);
}
8.5.1 泛型类和接口的使用(续)
符号,?,可以出现在泛型方法中:
public class Collections extends Object
{
public static <T> void copy(List<? super
T> dest,List<? extends T> src)
{
......
}
}
8.5.2 泛型方法的使用
使用泛型方法时,不必为其中的参数化类型指定实际的类型,Java编译器能依据调用方法时传递的实参推断出来。
例 8-8 使用泛型方法源代码 运 行
8.5.2 泛型方法的使用(续)
import java.util.*;
public class ErrorUseGenericMethod
{
public static<T> void
fromArrayListToHashSet(ArrayList<T> a,HashSet<T> h)
{
for(T o:a)
{
h.add(o);
}
}
public static void main(String args[])
{
ArrayList<String> as=new ArrayList<String>();
HashSet<Object> ho=new HashSet<Object>();
fromArrayListToHashSet(as,ho); //编译错误
}
}
8.5.2 泛型方法的使用(续)
上例中,方法 fromArrayListToHashSet()
的修改方案:
public static<T> void fromArrayListToHashSet
(ArrayList<T> a,HashSet<? super T> h)
{
for(T o:a)
{
h.add(o);
}
}
8.6 Java泛型的局限性
擦拭法:
– 去除尖括号中的所有参数化类型信息。
– 将所有参数化类型的变量替换成 Object。
– 消除参数化类型后,在所有类型不正确的地方,插入适当的类型转换。
注意,正因为 Java语言的泛型使用了“擦拭法”,程序中的参数化类型变量被转化为
Object,所以,它不能真正地代表“任何类型”,而只能代表 Object。
8.6 Java泛型的局限性(续)
public interface Collection<E> extends Iterable<E>
{
boolean add(E o);
......
}
被转换成:
public interface Collection extends Iterable
{
boolean add(Object o);
......
}
8.6 Java泛型的局限性(续)
List<String> ls=new ArrayList<String>();
ls.add("first");
ls.add("second");
String s=ls.get(0);
被转换成:
List ls=new ArrayList();
ls.add("first");
ls.add("second");
String s=(String)ls.get(0);
8.6 Java泛型的局限性(续)
错误:
class Dog
{
void talk(){}
}
class Cat
{
void talk(){}
}
public class ErrorDefGenerics
{
static<T> void speak(T speaker)
{
speaker.talk();
}
public static void main(String[] args)
{
Dog d=new Dog();
Cat c=new Cat();
speak(d);
speak(c);
}
}
8.6 Java泛型的局限性(续)
interface Speaks
{
void talk();
}
class Dog implements Speaks
{
public void talk(){}
}
class Cat implements Speaks
{
public void talk(){}
}
public class ErrorDefGenerics
{
static<T extends Speaks> void speak(T speaker)
{
speaker.talk();
}
public static void main(String[] args)
{
Dog d=new Dog();
Cat c=new Cat();
speak(d);
speak(c);
}
}
8.6 Java泛型的局限性(续)
上述代码中,方法 speak()可以改为:
static void speak(Speaks speaker)
{
speaker.talk();
}
上述代码中,使用泛型没有任何好处,甚至会使程序的可读性变差。
8.6 Java泛型的局限性(续)
不合法的代码:
static <T> T add(T a,T b)
{
return a+b;
}
Java语言引入泛型只是为了解决集合类中的自动类型转换,很少用于其他场合。
8.7 Autoboxing及 Unboxing
显式转换:
List<Integer> li = new
ArrayList<Integer>();
li.add(new Integer(0));
li.add(new Integer(1));
int i = li.get(0).intValue();
8.7 Autoboxing及 Unboxing(续)
自动转换,
List<Integer> li = new
ArrayList<Integer>();
li.add(0); //Autoboxing
li.add(1); //Autoboxing
int i = li.get(0); //Unboxing
8.8 Collection
泛型 Collection
– JDK 1.5引入
非泛型 Collection
– JDK 1.4.2及以前版本
8.8.1 泛型 Collection
public interface Collection<E> extends Iterable<E>
{
boolean add(E o);
boolean addAll(Collection<? extends E> c);
void clear();
boolean contains(Object o);
boolean containsAll(Collection<?> c);
boolean equals(Object o);
int hashCode();
boolean isEmpty();
boolean remove(Object o);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
int size();
Object[] toArray();
<T> T[] toArray(T[] a);
}
8.8.1 泛型 Collection(续)
Collection:
–List
add()方法一定会将指定元素添加到 List集合中。
–Set
add()方法只有在元素不重复的情况下才能将指定元素添加到 Set集合中(比如,HashSet,TreeSet)。
例 8-9 使用 Collection
源代码 运 行
8.8.2 非泛型 Collection
public interface Collection
{
boolean add(Object o);
boolean addAll(Collection c);
void clear();
boolean contains(Object o);
boolean containsAll(Collection c);
boolean equals(Object o);
int hashCode();
boolean isEmpty();
Iterator iterator();
boolean remove(Object o);
boolean removeAll(Collection c);
boolean retainAll(Collection c);
int size();
Object[] toArray();
Object[] toArray(Object[] a);
}
8.8.2 非泛型 Collection(续)
注意,编写新程序时,不应使用非泛型集合。
例 8-10 使用非泛型 Collection
源代码 运 行
8.9 List及其实现类
8.9.1 List的定义和使用
8.9.2 堆栈和队列
8.9.1 List的定义和使用
public interface List<E> extends Collection<E>
{
void add(int index,E element)
boolean addAll(int index,Collection<? extends E> c)
E get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
ListIterator<E> listIterator()
ListIterator<E> listIterator(int index)
E remove(int index)
E set(int index,E element)
List<E> subList(int fromIndex,int toIndex)
}
8.9.1 List的定义和使用(续)
public interface ListIterator<E> extends Iterator<E>
{
void add(E o)
boolean hasPrevious()
int nextIndex()
E previous()
int previousIndex()
void set(E o)
}
例 8-11 使用 List
源代码 运 行
8.9.1 List的定义和使用(续)
ArrayList
– 以数组为基础实现,支持快速随机访问 。
LinkedList
– 以双向链表为基础实现,支持元素的快速插入和移除 。
8.9.2 堆栈和队列
LinkedList:
–public void addFirst(E o)
–public void addLast(E o)
–public E getFirst()
–public E getLast()
–public E removeFirst()
–public E removeLast()
8.9.2 堆栈和队列(续)
堆栈是一种,后进先出,( LIFO) 的集合 。
队列是一种,先进先出,( FIFO) 的集合 。
例 8-12 堆栈例 8-13 定义堆栈类源代码 运 行源代码 运 行
8.10 Collections类
public static <T extends Comparable<? super T>>
void sort(List<T> list)
public static <T> int binarySearch(List<? extends
Comparable<? super T>> list,T key)
public static <T> void copy(List<? super T>
dest,List<? extends T> src)
public static <T extends Object & Comparable<? super
T>> T max(Collection<? extends T> coll)
public static <T extends Object & Comparable<? super
T>> T min(Collection<? extends T> coll)
public static void reverse(List<?> list)
8.10 Collections类(续)
JDK 1.5中,接口 Comparable的定义如下:
public interface Comparable<T>
{
int compareTo(T o);
}
例 8-14 使用 Collections类源代码 运 行
8.11 习 题