笔记汇总:《Java面向对象程序设计》学习笔记
# 第 13 章 泛型与集合框架
Java 提供了实现常见数据结构的类,这些实现数据结构的类通称为 Java 集合框架。
在 JDK1.5 后, Java 集合框架开始支持泛型,本章首先介绍泛型,然后讲解常见数据结构类的用法。
13.1 泛型
泛型 (Generics) 是在 JDK1.5 中推出的,其主要目的是可以建立具有类型安全的集合框架,如链表、散列映射等数据结构,本节主要对 Java 的泛型有一个初步的认识(基本够用)。
定义泛型类
使用 "class 名称〈泛型列表〉”声明一个类,为了和普通的类有所区别,这样声明的类称作泛型类,如:
class People<E>
其中 People 是泛型类的名称, E 是其中的泛型,
也就是说我们并没有指定 E 是何种类型的数据,它可以是任何对象或接口,但不能是基本类型数据。
也可以不用 E 表示泛型,使用任何一个合理的标识符都可以,但最好和我们熟悉的类型名称有所区别
// 不使用泛型
class Cone {
double height,
Circle bottom,
public Cone (Circle b) {
bottom = b;
}
}
// 使用泛型
class Cone<E> {
double height,
E bottom,
public Cone (E b) {
bottom = b;
}
}
泛型类声明对象
和普通的类相比,泛型类声明和创建对象时,类名后多了一对“ <> ”
而且要用具体的类型替换“ <> ”中的泛型(或使用统配?)。
用具体的类型
用具体的类型替换“ <> ”中的泛型,例如,用具体类型 CircIe 替换泛型 E :
Circle circle = new Circle();
Cone<Circle> coneOne; // 具体类型 Circle ,不可以用泛型 E : Cone<E> cone0ne;
coneOne = new Cone<Circle>(circle);
使用统配?
泛型类声明对象时,可以使用通配符?来限制泛型的范围。
例如,限制泛型 E 的范围:
Cone<? extends Geometry> coneOne;
如果 Geometry 是类,那么“<? extends Geometry >”中的“:? extends Geometry ”表示任何Geometry 类的子类或 Geometry 类本身(可理解为泛型 E 被限制了范围);
如果 Geometry 是接口,那么“<? extends Geometry >”中的“:? extends Geometry" 表示任何实现 Geometry 接口的类。
假设 Geometry 是接口, Circle 是实现了 Geometry 接口的类,那么下列创建 cone()ne 就是合法的:
Circle circle = new Circle();
coneOne = new Cone<CircIe>(circ1e);
再如:
Cone <? super B > coneOne;
这里 B 必须是一个类,不可以是接口,“<? super B >”中的“:? super B ”表示 B 类的任何父类(包括 B 类本身)。
泛型类声明对象时,也可以仅仅使用通配符“?”代表泛型 E ,但不限制泛型 E 的范围(”?“代表任意类型),创建对象时,必须用具体的类型,例如:
Circle circle = new Circle();
Cone<?> coneOne = new Cone<Circle>(circle);
小结
Java 泛型的主要目的是可以建立具有类型安全的数据结构,如链表、散列表等数据结构,
最重要的一个优点就是:在使用这些泛型类建立的数据结构时,不必进行强制类型转换,即不要求进行运行时类型检查。
JDK 1.5 是支持泛型的编译器,它将运行时的类型检查提前到编译时执行,使代码更安全。
Java 推出泛型的主要目的是为了建立具有类型安全的数据结构,如链表、散列映射等。
13.2 链表
数组和链表的区别
-
数组声明后大小固定,直接通过下标访问元素,因为下标连续,在内存中是相邻的。但不利于增删改。
-
链表查询慢,节点和节点的地址,在内存分配时不一定是相邻的。但是没个节点都能找到相邻的节点。
链表是由若干个(称作)节点(的对象)组成的一种数据结构。
-
单链表:每个结点含有一个数据和下一个结点的引用
-
双链表:或含有一个数据并含有上一个结点的引用和下一个结点的引用
13.2.1 LinkedList< E >泛型类
LinkedList 是实现了泛型接口 List 的泛型类,而泛型接囗 List又是 Collection 泛型接口的子接口。
LinkedList 泛型类中的绝大部分方法都是泛型接口方法的实现。
使用 LinkedList < E >泛型类声明和创建链表时,必须指定 E 的具体类型。
LinkedList < E >泛型类创建的对象以链表结构存储数据
LinkedList<String> mylist = new LinkedList<String>();
使用 add(E obj) 方法向链表依次增加结点。
mylist.add("How");
13.2.2 常用方法
P363
下面是 LinkedList < E >泛型类实现 List < E >泛型接口中的一些常用方法。
- public boolean add( E element) :向链表末尾添加一个新的结点,该结点中的数据是参数指定的数据。
- public void add(int index , E element ):向链表的指定位置添加一个新的结点,该结点中的数据是参数 element 指定的数据。
- public void clear() :删除链表的所有结点,使当前链表成为空链表。
- public E remove( int index) :删除指定位置上的结点。
- public boolean remove( E element) :删除首次出现含有数据 element 的结点。
- public E get(int index) :得到链表中指定位置处结点中的数据。
- public int indexOf(E element ):返回含有数据 element 的结点在链表中首次出现的位置,如果链表中无此结点则返回 -1 。
- public int lastlndexOf( E element) :返回含有数据 element 的结点在链表中最后出现的位置,如果链表中无此结点则返回 -1 。
- public E set( int index ,E element) :将当前链表 index 位置结点中的数据替换为参数element 指定的数据,并返回被替换的数据。
- public int size() :返回链表的长度,即结点的个数。
- public boolean contains(Object element) :判断链表结点中是否有结点含有数据element
下面是 LinkedList < E >泛型类本身新增加的一些常用方法。
- public void addFirst( E element ):向链表的头添加新结点,该结点中的数据是参数element 指定的数据。
- public void addLast(E element) :向链表的末尾添加新结点,该结点中的数据是参数elememt 指定的数据。
- public E getFirst() :得到链表中第一个结点中的数据。· public E getLast() :得到链表中最后一个结点中的数据。
- public E removeFirst() :删除第一个结点,并返回这个结点中的数据。· public E removeLast() :删除最后一个结点,并返回这个结点中的数据。
- public object clone() :得到当前链表的一个克隆链表,该克隆链表中结点数据的改变不会影响当前链表中结点的数据,反之亦然。
13.2.3 遍历链表
由于迭代器遍历集合的方法在找到集合中的一个对象的同时,也得到了待遍历的后继对象的引用。
因此,迭代器可以快速地遍历集合。
使用 iterator() 方法获取一个 lterator 对象,该对象就是针对当前链表的迭代器。
lterator < String > iter = list.iterator();
使用迭代器遍历链表和使用 get(int index) 方法遍历链表所用的时间对比
LinkedList<String> list=new LinkedList<String>();
for(int i=0;i<=60096;i++){
list.add("speed"+i);
}
Iterator<String> iter=list.iterator();
long starttime=System.currentTimeMillis();
while(iter.hasNext()){
String te=iter.next();
}
long endTime=System.currentTimeMillis();
long result=endTime-starttime;
System.out.println("使用迭代器遍历集合所用时间:"+result+"毫秒");
starttime=System.currentTimeMillis();
for(int i=0;i<list.size();i++){
String te=list.get(i);
}
endTime=System.currentTimeMillis();
result=endTime-starttime;
System.out.println("使用get方法遍历集合所用时间:"+result+"毫秒");
输出
使用迭代器遍历集合所用时间:4毫秒
使用get方法遍历集合所用时间:2109毫秒
动态数组表类 ArrayList
Java 也提供了序结构的动态数组表类 ArrayList 数组表采用序结构来存储数据。
数组表不适合动态地改变它存储的数据,如增加、删除单元等(比链表慢)。
但是,由于数组表采用顺序结构存储数据,数组表获得第 n 个单元中数据的速度要比链表获得第 n 个单元中数据的速度快。
ArrayList 类的很多方法与 LinkedList 类似,二者的本质区别是一个使用顺序结构,一个使用链式结构。
但删除节点、增加结点没有 LinkedList 快。
需要注意的是,使用 get()获取一个结点中的对象时,要用类型转换运算符转换回原来的类型。
LinkedList mylist=new LinkedList();
mylist.add("你"); //链表中的第一个节点
mylist.add("好"); //链表中的第二个节点
int number=mylist.size(); //获取链表的长度
for(int i=0;i<number;i++){
String temp=(String)mylist.get(i); //必须强制转换取出的数据
System.out.println("第"+i+"节点中的数据:"+temp);
}
Iterator iter=mylist.iterator();
while(iter.hasNext()) {
String te=(String)iter.next(); //必须强制转换取出的数据
System.out.println(te);
}
其他的链表操作,在算法那里再进行学习和记录吧。
13.3 堆栈
堆栈是一种“后进先出”的数据结构,只能在一端进行输入或输出数据的操作。
堆栈把第一个放入该堆栈的数据放在最底下,而把后续放入的数据放在己有数据的顶上。
向堆栈中输入数据的操作称为“压栈”,从堆栈中输出数据的操作称为“弹栈”。
由于堆栈总是在顶端进行数据的输入输出操作,所以弹栈总是输出(删除)最后压入堆栈中的数据,这就是“后进先出”的来历。
常用方法
使用 Stack < E >泛型类创建一个堆栈对象
堆栈对象可以
使用 public E push(E item) ; 实现压栈操作;
使用 public E pop(); 实现弹栈操作;
使用 public boolean empty(); 判断堆栈是否还有数据,有数据返回 false ,否则返回 true ;
使用 public E peek(); 获取堆栈顶端的数据,但不删除该数据;
使用 public int search(Object data) ; 获取数据在堆栈中的位置,最顶端的位置是 1 ,向下依次增加,如果堆栈不含此数据,则返回 -1 。
优点
堆栈是很灵活的数据结构,使用堆栈可以节省内存的开销。
例如,递归是一种很消耗内存的算法,可以借助堆栈消除大部分递归,达到和递归算法同样的目的。
用堆栈输出该递归序列的若干项
Stack<Integer> stack=new Stack<Integer>();
stack.push(1);
stack.push(1);
int k=1;
while(k<=10) {
for(int i=1;i<=2;i++) {
int f1=stack.pop();
int f2=stack.pop();
int next = f1+f2;
System.out.println(""+next);
stack.push(next);
stack.push(f2);
k++;
}
}
13.4 散列映射
13.4.1 HashMap < K,V > 泛型类
HashMap<K,V> 泛型类实现了泛型接口 Map<K,V>
HashMap<K,V> 类中的绝大部分方法都是 Map<K,V> 接口方法的实现。
HashMap<K,V> 对象用于存储 “键/值”对,允许把任何数量的 “键/值”对 存储在一起。
键不可以发生逻辑冲突,即不要两个数据项使用相同的键,如果出现两个数据项对应相同的键,那么,先前散列映射中的 “键/值”对 将被替换。
散列映射在需要更多的存储空间时会自动增大容量。
使用散列映射来存储要查找的数据,使用散列映射可以减少检索的开销。
13.4.2 HashMap < K,V > 泛型类的常用方法
P371
- public void clear() :清空散列映射。
- public Object clone() :返回当前散列映射的一个克隆。
- public boolean containsKey(Object key) :如果散列映射有“键/值”对使用了参数指定的键,则该方法返回 true ,否则返回 false。
- public boolean containsValue(Object value) :如果散列映射有“键/值”对的值是参数指定的值,则该方法返回 true ,否则返回 false。
- public V get(Object key) :返回散列映射中使用 key 作为键的“键/值”对中的值。
- public boolean isEmpty( ):如果散列映射不含任何“键/值”对,则该方法返回 true ,否则返回 false。
- public v remove( Object key) :删除散列映射中键为参数指定的“键/值”对,并返回键对应的值。
- public int size() :返回散列映射的大小,即散列映射中“键/值”对的数目。
13.4.3 遍历散列映射
public Collection < V > values()方法返回一个实现 Collection < V >接口类创建的对象,可以使用接口回调技术,即将该对象的引用赋给 Collection < V >接口变量,
该接口变量可以回调iterator()方法获取一个 lterator 对象,这个 lterator 对象存放着散列映射中所有“键/值”对中的“值”。
13.4.4 基于散列映射的查询
对于经常需要进行查找的数据可以采用散列映射来存储,即为数据指定一个查找它的关键字,然后按照“键/值”对将关键字和数据一起存人散列映射中。
根据ISBN查询书籍
import java.util.*;
class Book {
String ISBN,name;
Book(String ISBN ,String name){
this.name =name;
this.ISBN=ISBN;
}
}
public class Hashmap {
public static void main(String[] args) {
// 创建三个Book对象
Book book1 = new Book("7302033218","Java 面向对象程序设计"),
book2 = new Book("7808315162","JSP程序设计"),
book3 = new Book("7302054991","Java设计模式");
// 创建HashMap实例,键为ISBN,值为Book对象
HashMap<String,Book> table = new HashMap<String,Book>();
// 将Book对象添加到HashMap中
table.put(book1.ISBN,book1);
table.put(book2.ISBN,book2);
table.put(book3.ISBN,book3);
// 指定键值
String key = "7808315162";
// 检查HashMap中是否包含指定键值
if(table.containsKey(key)) {
// 根据键值获取对应的Book对象
Book book = table.get(key);
System.out.println(book.name+" 有货");
}
// 获取散列映射中的元素数量
int number = table.size();
System.out.println("散列映射中有"+ number +"个元素:");
// 获取散列映射中的所有值的集合
Collection<Book> collection = table.values();
// 创建迭代器遍历集合中的元素
Iterator<Book> iter = collection.iterator();
while(iter.hasNext()){
// 获取当前迭代的Book对象
Book te = iter.next();
System.out.printf("书名:%s,ISBN:%s\n", te.name , te.ISBN);
}
}
}
输出
JSP程序设计 有货
散列映射中有3个元素:
书名:Java设计模式,ISBN:7302054991
书名:JSP程序设计,ISBN:7808315162
书名:Java 面向对象程序设计,ISBN:7302033218
13.5 树集
13.5.1 TreeSet < E >泛型类
TreeSet< E > 类是实现 Set < E >接口的类,它的大部分方法都是接口方法的实现。
TreeSet < E > 类创建的对象称为树集。
树集采用树结构存储数据,树结点中的数据会按存放数据的“大小”顺序一层一层地依次排列,在同一层中的结点从左到右按字典序从小到大的顺序递增排列,下一层的都比上一层的小。
理论上,把一个元素插入树集的合适位置要比插入数组或链表中的合适位置效率高。
13.5.2 结点的大小关系
实现 Comparable 接口类创建的对象可以调用 compareTo (Object str) 方法和参数指定的对象比较大小关系。
假如 a 和 b 是实现 Comparable 接口类创建的两个对象,当 a. compareTo (b)< 0时,称 a 小于 b ;当 a. compareTo ( b)> 0 时,称 a 大于 b ;当 a. compareTo ( b )= 0 时,称 a 等于 b。
13.5.3 TreeSet 类的常用方法
- public boolean add(E o) :向树集添加结点,结点中的数据由参数指定,若添加成功则返回 true ,否则返回 false。
- public void clear() :删除树集中的所有结点。
- public void contains(Object o):如果树集中有包含参数指定的对象,则该方法返回true ,否则返回 false。
- public E first () :返回树集中第一个结点中的数据(最小的结点)。
- public E last() :返回最后一个结点中的数据(最大的结点)。
- public isEmpty() :判断是否是空树集,如果树集不含任何结点,该方法返回 true。
- public boolean remove( Object o):删除树集中存储参数指定的对象的最小结点,如果删除成功,则该方法返回 true ,否则返回 false。
- public int size() :返回树集中结点的数目。
树集按照英语成绩从低到高存放 4 个Student 对象。
import java.util.*;
class Student implements Comparable {
int english=0;
String name;
Student(int english,String name) {
this.name=name;
this.english=english;
}
public int compareTo(Object b) {
Student st=(Student)b;
return (this.english-st.english);
}
}
public class Example13_8 {
public static void main(String args[]) {
TreeSet<Student> mytree=new TreeSet<Student>();
Student st1,st2,st3,st4;
st1=new Student(90,"赵一");
st2=new Student(66,"钱二");
st3=new Student(86,"孙三");
st4=new Student(76,"李四");
mytree.add(st1);
mytree.add(st2);
mytree.add(st3);
mytree.add(st4);
Iterator<Student> te=mytree.iterator();
while(te.hasNext()) {
Student stu=te.next();
System.out.println(""+stu.name+" "+stu.english);
}
}
}
树集中不允许出现大小相等的两个结点。
例如,在上述代码中如果再添加语句:
st5 = new Student(76,"keng wenyi");
mytree.add(st5);
是无效的。如果允许成绩相同,可把上述代码中 Student 类的 compareTo 方法更改为:
public int compareTo(Object b) {
Student st = (Student)b;
if((this.english - st.English) == 0)
return 1;
else
return (this.english - st.English);
}
13.6 树映射
TreeMap <K,V> 类实现了 Map <K,V> 接口,称 TreeMap <K,V> 对象为树映射。
树映射使用
public V put (K key,V value);
方法去添加节点,该节点不仅存储着数据 value ,而且也存储着和其关联的关键字 key 。
也就是说,树映射的节点存储“关键字/值”对。
和树集不同的是,树映射保证节点是按照节点中的关键字升序排列。
使用了 TreeMap ,分别按照学生的英语成绩和数学成绩排序结点。
import java.util.*;
class StudentKey implements Comparable {
double d=0;
StudentKey (double d) {
this.d=d;
}
public int compareTo(Object b) {
StudentKey st=(StudentKey)b;
if((this.d-st.d)==0)
return -1;
else
return (int)((this.d-st.d)*1000);
}
}
class Student {
String name=null;
double math,english;
Student(String s,double m,double e) {
name=s;
math=m;
english=e;
}
}
public class Example13_9 {
public static void main(String args[ ]) {
TreeMap<StudentKey,Student> treemap= new TreeMap<StudentKey,Student>();
String str[]={"赵一","钱二","孙三","李四"};
double math[]={89,45,78,76};
double english[]={67,66,90,56};
Student student[]=new Student[4];
for(int k=0;k<student.length;k++) {
student[k]=new Student(str[k],math[k],english[k]);
}
StudentKey key[]=new StudentKey[4] ;
for(int k=0;k<key.length;k++) {
key[k]=new StudentKey(student[k].math); //关键字按数学成绩排列大小
}
for(int k=0;k<student.length;k++) {
treemap.put(key[k],student[k]);
}
int number=treemap.size();
System.out.println("树映射中有"+number+"个对象,按数学成绩排序:");
Collection<Student> collection=treemap.values();
Iterator<Student> iter=collection.iterator();
while(iter.hasNext()) {
Student stu=iter.next();
System.out.println("姓名 "+stu.name+" 数学 "+stu.math);
}
treemap.clear(); // 清空之后再放,否则会乱套
for(int k=0;k<key.length;k++) {
key[k]=new StudentKey(student[k].english);//关键字按英语成绩排列大小
}
for(int k=0;k<student.length;k++) {
treemap.put(key[k],student[k]);
}
number=treemap.size();
System.out.println("树映射中有"+number+"个对象:按英语成绩排序:");
collection=treemap.values();
iter=collection.iterator();
while(iter.hasNext()) {
Student stu=(Student)iter.next();
System.out.println("姓名 "+stu.name+" 英语 "+stu.english);
}
}
}
输出
树映射中有4个对象,按数学成绩排序:
姓名 钱二 数学 45.0
姓名 李四 数学 76.0
姓名 孙三 数学 78.0
姓名 赵一 数学 89.0
树映射中有4个对象:按英语成绩排序:
姓名 李四 英语 56.0
姓名 钱二 英语 66.0
姓名 赵一 英语 67.0
姓名 孙三 英语 90.0
13.7 自动装箱与拆箱的使用
基本类型数据和相应的对象之间相互自动转换的功能,称为基本数据类型的自动装箱与拆箱( Autoboxing and Auto-Unboxing of Primitive Types)。
在没有自动装箱与拆箱功能之前,不能将基本数据类型数据添加到类似链表的数据结构中。
程序允许把一个基本数据类型添加到类似链表等数据结构中,系统会自动完成基本类型到相应对象的转换(自动装箱)。
当从一个数据结构中获取对象时,如果该对象是基本数据的封装对象,那么系统将自动完成对象到基本类型的转换(自动拆箱 )。
使用了自动装箱与拆箱。
import java.util.*;
public class Example13_10 {
public static void main(String args[]) {
ArrayList<Integer> list=new ArrayList<Integer>();
for(int i=0;i<10;i++) {
list.add(i); //自动装箱,实际添加到list中的是new Integer(i)。
}
for(int k=list.size()-1;k>=0;k--) {
int m=list.get(k); //自动拆箱,获取Integer对象中的int型数据
System.out.printf("%3d",m);
}
}
}
输出
9 8 7 6 5 4 3 2 1 0
13.8 集合
HashSet < E >泛型类在数据组织上类似于数学上的集合,可以进行“交” “并” “差”等运算。
13.8.1 HashSet < E >泛型类
HashSet < E >泛型类创建的对象称为集合。
HashSet <String> set = HashSet <String>();
set 就是一个可以存储 String 类型数据的集合
可以调用 add(String s)方法将 String类型的数据添加到集合中,添加到集合中的数据称作集合的元素。
集合不允许有相同的元素
集合的容量会自动扩充
13.8.2 常用方法
- public boolean add(E o) :向集合添加参数指定的元素。
- public void clear() :清空集合,使集合不含有任何元素。
- public boolean contains(Object o):判断参数指定的数据是否属于集合。
- public boolean isEmpty() :判断集合是否为空。
- public boolean remove(Object o):集合删除参数指定的元素。
- public int size():返回集合中元素的个数。
- Object[] toArray( ):将集合元素存放到数组中,并返回这个数组。
- boolean containsAll(HanshSet set) :判断当前集合是否包含参数指定的集合。
- public Object clone():得到当前集合的一个克隆对象,该对象中元素的改变不会影响到当前集合中的元素,反之亦然。
可以借助泛型类 lterator < E >实现遍历集合
使用 next() 方法遍历集合
把学生的成绩存放在一个集合中,并实现了遍历集合。
import java.util.*;
class Student{
String name;
int score;
Student(String name,int score){
this.name=name;
this.score=score;
}
}
public class Example13_11{
public static void main(String args[]){
Student zh=new Student("张三",76),
wa=new Student("王二",88),
li=new Student("李四",97);
HashSet<Student> set=new HashSet<Student>();
HashSet<Student> subset=new HashSet<Student>();
set.add(zh);
set.add(wa);
set.add(li);
subset.add(wa);
subset.add(li);
if(set.contains(wa)){
System.out.println("集合set中含有:"+wa.name);
}
if(set.containsAll(subset)){
System.out.println("集合set包含集合subset");
}
int number=subset.size();
System.out.println("集合subset中有"+number+"个元素:");
Object s[]=subset.toArray();
for(int i=0;i<s.length;i++){
System.out.printf("姓名:%s,分数:%d\n",((Student)s[i]).name,((Student)s[i]).score);
}
number=set.size();
System.out.println("集合set中有"+number+"个元素:");
Iterator<Student> iter=set.iterator();
while(iter.hasNext()){
Student te = iter.next();
System.out.printf("姓名:%s,分数:%d\n",te.name,te.score);
}
}
}
输出
集合set中含有:王二
集合set包含集合subset
集合subset中有2个元素:
姓名:李四,分数:97
姓名:王二,分数:88
集合set中有3个元素:
姓名:李四,分数:97
姓名:张三,分数:76
姓名:王二,分数:88
13.8.3 集合的交、并与差
集合对象调用
- boolean retainAll (HashSet set) 方法可以与参数指定的集合求交运算,使得当前集合成为两个集合的交。
- boolean addAll (HashSet set) 方法可以与参数指定的集合求并运算,使得当前集合成为两个集合的并。
- boolean removeAll (HashSet set) 方法可以与参数指定的集合求差运算,使得当前集合成为两个集合的差。
参数指定的集合必须与当前集合是同种类型的集合,否则上述方法返回 false。
求两个集合 A 、 B 的对称差集合,即求 A- B与 B-A 的合集 A-B ∪ B-A 。
import java.util.*;
public class Example13_12{
public static void main(String args[]){
HashSet<Integer> A=new HashSet<Integer>(),
B=new HashSet<Integer>();
for(int i=1;i<=4;i++){
A.add(i);
}
B.add(1);
B.add(2);
B.add(5);
B.add(6);
HashSet<Integer> tempSet=(HashSet<Integer>)A.clone();
A.removeAll(B); //A变成调用该方法之前的集合A与集合B的差集 A-B
B.removeAll(tempSet); //B变成调用该方法之前的集合B与集合tempSet的差集 B-A
B.addAll(A); //B就是最初的A与B的对称差 A-B∪B-A
int number=B.size();
System.out.println("A和B的对称差集合有"+number+"个元素:");
Iterator<?> iter = B.iterator();
while(iter.hasNext()){
System.out.printf("%d,",iter.next());
}
}
}
输出
A和B的对称差集合有4个元素:
3,4,5,6,
13.9 小结
- 使用 "class 名称<泛型列表>”声明一个泛型类,当使用泛型类声明对象时,必须用具体的类型(不能是基本数据类型)替换泛型列表中的泛型。
- LinkedList < E >泛型类创建的对象以链表结构存储数据,链表是由若干个被称为结点的对象组成的一种数据结构,每个结点含有一个数据以及上一个结点的引用和下一个结点的引用。
- Stack < E >泛型类创建一个堆栈对象,堆栈把第一个放人该堆栈的数据放在最底下,而把后续放人的数据放在已有数据的上面,堆栈总是在顶端进行数据的输人/输出操作。
- HashMap < K , V >泛型类创建散列映射,散列映射采用散列表结构存储数据,用于存储键/值数据对,允许把任何数量的键/值数据对存储在一起。使用散列映射存储经常需要检索的数据,可以减少检索的开销。
- TreeSet < E >类创建树集,树集结点的排列和链表不同,不按添加的先后顺序排列,当一个树集中的数据是实现 Co mparable 接口类创建的对象时,结点将按对象的大小关系升序排列。
- TreeMap < K , V >类创建树映射,树映射的结点存储“键/值”对,和树集不同的是,树映射保证结点是按照结点中的“键”升序排列。