11Java集合.docx
《11Java集合.docx》由会员分享,可在线阅读,更多相关《11Java集合.docx(61页珍藏版)》请在冰豆网上搜索。
11Java集合
11Java集合
11.1认识集合类
集合(或容器)表示保存一个对象组的单个对象,其它对象被认为是它的元素。
集合与数组的不同在于,它是大小可变的序列,而且元素类型可以不受限定,只要是引用类型。
对象数组中包含一组对象,但是对象数组使用的时候存在一个长度的限制,那么集合是专门解决这种限制的,使用集合可以方便的向数组中增加任意多个数据。
集合类使用初始容量和加载因子调整自己的大小。
集合类全部支持泛型,是一种数据安全的用法。
Java集合框架定义了几个接口。
这些集合接口决定了集合实现类的基本特性。
对于不同的集合实现类提供了这些标准接口的不同实现。
Java集合接口如表11-1所示:
表11-1Java集合接口
Java集合类库的用途就是“保存对象”,主要分为两类:
Collection:
层次结构中的根接口。
Collection表示一组对象,这些对象也称为collection的元素。
一些collection允许有重复的元素,而另一些则不允许。
一些collection是有序的,而另一些则是无序的。
JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现。
此接口通常用来传递collection,并在需要最大普遍性的地方操作这些collection。
Collection常见的子接口如图11-1:
图11-1Collection接口继承图
Collection:
一组没有顺序的对象,允许出现相同的元素对象。
Set:
一组没有顺序的对象(离散),不允许重复相同的元素。
SortedSet:
是一个按照升序排列元素的Set。
List:
一组有插入顺序的对象,允许重复相同的元素。
关于Collection更为详细的继承图谱如图11-2:
图11-2Collection接口以及实现类继承图谱
Map:
一组成对的“键—值”对象,允许你使用键来查找值。
ArrayList允许你使用数字来查找值,因此在某种意义上讲,它将数组与对象关联在一起。
映射表允许我们使用一个对象来查找某个对象,它被称为“关联数组”,因为它将某些对象与另外一些对象关联在一起,也被称为“字典”,因为你可以使用键对象来查找值对象。
Map常见的子接口如图11-3:
图11-3Map接口继承图
关于Map更为详细的继承图谱如图11-4:
图11-4Map接口以及实现类继承图谱
除了类集接口之外,类集也使用Comparator、Iterator和ListIterator接口。
关于这些接口将在本章后面做更深入的讲解。
简单地说,Comparator接口定义了两个对象比较方法。
Iterator和ListIterator接口类集中的对象。
为了在它们的使用中提供最大的灵活性,类集接口允许对一些方法进行选择。
可选择的方法使得使用者可以更改类集的内容。
支持这些方法的类集被称为可修改的(modifiable)。
不允许修改其内容的类集被称为不可修改的(unmodifiable)。
而所有内置的类集都是可修改的。
如果对一个不可修改的类集使用这些方法,将引发一个UnsupportedOperationException异常。
11.2Collection接口及其子接口
Collection接口是构造类集框架的基础,是保存单值集合的最大父接口。
Java没有提供该接口的直接实现类。
Collection接口定义如下:
publicinterfaceCollectionextendsIterable
JDK1.5之后为Collection接口增加了泛型声明。
所有的类集操作都存放在java.util包中。
在一般的开发中,往往很少去直接使用Collection接口进行开发,而基本上都是使用其子接口。
常见的Collection子接口有:
List、Set、Queue、SortedSet。
Collection声明了所有类集都将拥有的核心方法。
在JDK1.5之前,这些方法被总结在表11-2(A)中,在JDK1.5之后,增加了泛型,这些方法被总结在表11-2(B)中。
因为所有类集实现Collection,所以熟悉它的方法对于清楚地理解框架是必要的。
其中几种方法可能会引发一个UnsupportedOperationException异常。
正如上面的解释,这些将发生在当类集不能被修改的时候。
当一个对象与另一个对象不兼容,例如:
当企图增加一个不兼容的对象到一个类集中时,将产生一个ClassCastException异常。
表11-2(A)JDK1.5之前Collection接口的方法定义
表11-2(B)JDK1.5之后Collection接口的方法定义
(注:
关于JDK1.5后方法的具体说明可以参考JDK1.5之前的方法说明)
调用add()方法可以将对象加入类集。
注意add()带一个泛型类型的参数。
因为Object是所有类的超类,所以任何类型的对象可以被存储在一个Collection
可以通过调用addAll()方法将一个类集的全部内容增加到另一个类集中。
可以通过调用remove()方法将一个对象删除。
为了删除一组对象,可以调用removeAll()方法。
调用retainAll()方法可以将除了一组指定的元素之外的所有元素删除。
为了清空类集,可以调用clear()方法。
通过调用contains()方法,可以确定一个类集是否包含了一个指定的对象。
为了确定一个类集是否包含了另一个类集的全部元素,可以调用containsAll()方法。
当一个类集是空的时候,可以通过调用isEmpty()方法来予以确认。
调用size()方法可以获得类集中当前元素的个数。
toArray()方法返回一个数组,这个数组包含了存储在调用类集中的元素。
这个方法比它初看上去的能力要更重要。
经常使用类数组语法来处理类集的内容是有优势的。
通过在类集和数组之间提供一条路径,可以充分利用这两者的优点。
调用equals()方法可以比较两个类集是否相等。
“相等”的精确含义可以不同于从类集到类集。
例如,可以执行equals()方法以便用于比较存储在类集中的元素的值,换句话说,equals()方法能比较对象元素的引用。
一个更加重要的方法是iterator(),该方法对类集返回一个迭代程序。
我们可以通过它遍历整个集合。
除此之外还可以利用foreach结构遍历。
11.2.1List接口
List是Collection的子接口,它强调元素的插入顺序,可以包含重复的元素。
它扩展了Collection接口并提供了按索引访问元素的方式,可以利用索引下标在集合的指定位置插入、删除和访问元素。
实现List接口的类有:
ArrayList、LinkedList、Stack、Vector。
其中Stack和Vector是早期集合类,他们是线程安全(同步的)集合类,一般性能会稍慢于非同步的集合类。
ArrayList是利用数组实现的,LinkedList是利用链表实现的。
List除了由Collection定义的方法之外,List还定义了一些它自己的方法,这些方法总结在表11-3中。
需要再次注意当类集不能被修改时,其中的几种方法引发UnsupportedOperationException异常。
当一个对象与另一个不兼容,例如:
当企图将一个不兼容的对象加入一个类集中时,将产生ClassCastException异常。
表11-3由List定义的方法
对于由Collection定义的add()和addAll()方法,List增加了方法add(int,Object)和addAll(int,Collection)。
这些方法在指定的下标处插入元素。
由Collection定义的add(Object)和addAll(Collection)的语义也被List改变了,用于在列表的尾部增加元素。
为了获得指定位置的存储对象,可以利用get(下标)的形式得到。
为了给列表中的一个元素赋值,可以调用set()方法,指定被改变的对象的下标。
调用indexOf()或lastIndexOf()可以得到一个对象的下标。
通过调用subList()方法,可以获得列表的一个指定了开始下标和结束下标的子列表。
对于List而言,除了有foreach遍历方式和迭代器遍历方式之外,还可以利用get()方法通过循环完成遍历,循环次数由size()决定,此遍历操作是List接口独有的。
11.2.2Set接口
Set继承于Collection,由于其不允许元素重复,没有顺序概念,相对于Collection无需扩展方法,限制add类方法不要放入重复元素就可以(set判断两个对象是否相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相同)。
因此,如果试图将重复元素加到集合中时,add()方法将返回false。
实际创建时要使用Set的实现类,包括EnumSet,HashSet,LinkedHashSet,TreeSet,其中LinkedHashSet体现插入顺序,TreeSet体现了元素排序。
性能方面“HashSet>LinkedHashSet>TreeSet”,但是对于遍历查找则可能相反。
11.2.2.1SortedSet接口
SortedSet接口扩展了Set并说明了按升序排列的集合的特性。
除了那些由Set定义的方法之外,由SortedSet接口说明的方法列在表11-4中。
当没有项包含在调用集合中时,其中的几种方法会引发NoSuchElementException异常。
当对象与调用集合中的元素不兼容时,将引发ClassCastException异常。
如果试图使用null对象,而集合不允许null时,会引发NullPointerException异常。
表11-4由SortedSet定义的方法
SortedSet定义了几种方法,使得对集合的处理更加方便。
调用first()方法,可以获得集合中的第一个对象。
调用last()方法,可以获得集合中的最后一个元素。
调用subSet()方法,可以获得排序集合的一个指定了第一个和最后一个对象的子集合。
如果需要得到从集合的第一个元素开始的一个子集合,可以使用headSet()方法。
如果需要获得集合尾部的一个子集合,可以使用tailSet()方法。
11.3Collection接口及其子接口的常见实现类
现在,大家已经熟悉了类集接口,下面开始讨论实现它们的标准类。
一些类提供了完整的可以被使用的工具。
另一些类是抽象的,提供主框架工具,作为创建具体类集的起始点。
没有一个Collection接口是同步的,在本章后面看到的那样,有可能获得同步版本。
标准的Collection实现类总结表11-5中。
表11-5Collection实现类
注意:
除了Collection接口之外,还有几个从以前版本遗留下来的类,如Vector、Stack和Hashtable均被重新设计成支持类集的形式。
这些内容将在本章后面讨论。
下面讨论具体的Collection接口,举例说明它们的用法。
11.3.1ArrayList类
ArrayList类扩展AbstractList并支持List接口。
ArrayList支持可随需要而增长的动态数组。
在Java中,标准数组是定长的。
在数组创建之后,它们不能被加长或缩短,这也就意味着开发者必须事先知道数组可以容纳多少元素。
但是,一般情况下,只有在运行时才能知道需要多大的数组。
为了解决这个问题,类集框架定义了ArrayList。
本质上,ArrayList是对象引用的一个变长数组。
也就是说,ArrayList能够动态地增加或减小其大小。
数组列表以一个原始大小被创建。
当超过了它的大小,类集自动增大。
当对象被删除后,数组就可以缩小。
注意:
动态数组也被从以前版本遗留下来的类Vector所支持。
关于这一点,将在后面介绍。
ArrayList有如下的构造方法:
ArrayList()
ArrayList(Collectionc)
ArrayList(intcapacity)
其中第一个构造方法建立一个空的数组列表。
第二个构造方法建立一个数组列表,该数组列表由集合c中的元素初始化。
第三个构造函数建立一个数组列表,该数组有指定的初始容量(capacity)。
容量是用于存储元素的基本数组的大小。
当元素被追加到数组列表上时,容量会自动增加。
下面的程序是ArrayList的一个简单应用。
首先创建一个数组列表,接着添加String类型的对象(回想一个引用字符串被转化成一个字符串(String)对象的方法)。
接着列表被显示出来。
将其中的一些元素删除后,再一次显示列表。
范例:
ArrayListDemo.java
importjava.util.*;
publicclassArrayListDemo{
publicstaticvoidmain(Stringargs[]){
//创建一个ArrarList对象
ArrayListal=newArrayList();
System.out.println("a1的初始化大小:
"+al.size());
//向ArrayList对象中添加新内容
al.add("C");//0位置
al.add("A");//1位置
al.add("E");//2位置
al.add("B");//3位置
al.add("D");//4位置
al.add("F");//5位置
//把A2加在ArrayList对象的第2个位置
al.add(1,"A2");//加入之后的内容:
CA2AEBDF
System.out.println("a1加入元素之后的大小:
"+al.size());
//显示Arraylist数据
System.out.println("a1的内容:
"+al);
//从ArrayList中移除数据
al.remove("F");
al.remove
(2);//CA2EBD
System.out.println("a1删除元素之后的大小:
"+al.size());
System.out.println("a1的内容:
"+al);
}
}
输出结果:
a1的初始化大小:
0
a1加入元素之后的大小:
7
a1的内容:
[C,A2,A,E,B,D,F]
a1删除元素之后的大小:
5
a1的内容:
[C,A2,E,B,D]
注意a1开始时是空的,当添加元素后,它的大小增加了。
当有元素被删除后,它的大小又会变小。
在前面的例子中,使用由toString()方法提供的默认的转换显示类集的内容,toString()方法是从AbstractCollection继承下来的。
它对简短的程序来说是足够了,但很少使用这种方法去显示实际中的类集的内容。
通常编程者会提供自己的输出程序。
但在下面的几个例子中,仍将采用由toString()方法创建的默认输出。
尽管当对象被存储在ArrayList对象中时,其容量会自动增加。
然而,也可以通过调用ensureCapacity()方法来人工地增加ArrayList的容量。
如果事先知道将在当前能够容纳的类集中存储许许多多的内容时,你可能会想这样做。
在开始时,通过一次性地增加它的容量,就能避免后面的再分配。
因为再分配是很花时间的,避免不必要的处理可以提高性能。
范例:
ArrayListToArray.java
importjava.util.*;
publicclassArrayListToArray{
publicstaticvoidmain(Stringargs[]){
//创建一个ArrayList对象al
ArrayListal=newArrayList();
//向ArrayList中加入对象
al.add(newInteger
(1));
al.add(newInteger
(2));
al.add(3);
al.add(4);
System.out.println("ArrayList中的内容:
"+al);
//得到对象数组
Objectia[]=al.toArray();
intsum=0;
//计算数组内容
for(inti=0;isum+=((Integer)ia[i]).intValue();
System.out.println("数组累加结果是:
"+sum);
}
}
输出结果:
ArrayList中的内容:
[1,2,3,4]
数组累加结果是:
10
程序开始时创建一个整数的类集。
接下来,toArray()方法被调用,它获得了一个Object对象数组。
这个数组的内容被置成整型(Integer),接下来对这些值进行求和操作。
11.3.2LinkedList类
LinkedList类扩展了AbstractSequentialList类并实现List接口。
它提供了一个链接列表的数据结构。
它具有如下的两个构造方法,说明如下:
LinkedList()
LinkedList(Collectionc)
第一个构造方法建立一个空的链接列表。
第二个构造方法建立一个链接列表,该链接列表由另一个集合c中的元素初始化。
除了它继承的方法之外,LinkedList类本身还定义了一些有用的方法,这些方法主要用于操作和访问列表。
使用addFirst()方法或offerFirst()方法或add(0,obj)方法或push()方法可以在列表头增加元素。
它们的形式如下所示:
voidaddFirst(Eobj)
voidadd(intindex,Ee)
booleanofferFirst(Ee)
voidpush(Ee)
使用addLast()方法或offer()方法或offerLast()可以在列表的尾部增加元素。
它们的形式如下所示:
voidaddLast(Eobj)
booleanoffer(Ee)
booleanofferLast(Fe)
调用getFirst()方法或element()方法或get(0)方法或peek()方法或peekFirst()方法可以获得第一个元素,且不从列表中删除该元素。
它们的形式如下所示:
EgetFirst()
Eelement()
Eget(intindex)
Epeek()
EpeekFirst()
调用getLast()方法或get(link.size()-1)方法或peekLase()方法可以得到最后一个元素。
注意这些方法都是获得但不移除。
它们的形式如下所示:
EgetLast()
Eget(intindex)
EpeekLast
为了获取并删除第一个元素,可以使用remove()或remove(0)或removeFirst()或poll()或pollFirst()或pop()方法。
它们的形式如下所示:
Eremove()
Eremove(intindex)
EremoveFirst()
Epoll()
EpollFrist()
Epop()
为了删除最后一个元素,可以调用remove(link.size()-1)或removeLast()或pollLast()方法。
它们的形式如下所示:
Eremove(intindex)
EremoveLast()
EpollLast()
调用removeFirstOccurrence(e)方法用于删掉从头到尾找第一次出现该元素的元素,调用removeLastOccurrence(e)方法用于删掉从后往前找第一次出现该元素的元素。
它们的形式如下所示:
booleanremoveFirstOccurrence(Objecte)
booleanremoveLastOccurrence(Objecte)
利用正常迭代器Iterator可以正序遍历整个集合,利用iterator()方法:
//LinkedList正常迭代器访问方式
Iteratorit=link.iterator();
while(it.hasNext()){
System.out.print(it.next()+"");
}
System.out.println();
利用ListIterator可以从指定位置向前或向后遍历集合,利用listIterator(intindex)方法:
//LinkedList从指定位置开始向后遍历
ListIteratorlit=link.listIterator(3);
while(lit.hasNext()){
System.out.print(lit.next()+"");
}
System.out.println();
//LinkedList从指定位置开始向前遍历
ListIteratorrlit=link.listIterator(3);
while(rlit.hasPrevious()){
System.out.print(rlit.previous()+"");
}
System.out.println();
利用正常迭代器Iterator可以逆序遍历整个集合,利用descendingIterator()方法:
//LinkedList反向迭代器
Iteratordit=link.descendingIterator();
while(dit.hasNext()){
System.out.print(dit.next()+"");
}
System.out.println();
下面的程序举例是对几个LinkedList支持的方法的说明:
范例:
LinkedListDemo.java
importjava.util.*;
publicclassLinkedListDemo{
publicstaticvoidmain(Stringargs[]){
//创建LinkedList对象
LinkedListll=newLinkedList();
//加入元素到LinkedList中
ll.add("F");
ll.add("B");
ll.add("D");
ll.add("E");
ll.add("C");
//在链表的最后个位置加上数据
ll.addLast("Z");
//在链表的第一个位置上加入数据
ll.addFirst("A");
//在链表第二个元素的位置上加入数据
ll.add(1,"A2");
System.out.println("ll最初的内容:
"+ll);
//从linkedlist中移除元素
ll.remove("F");
ll.remove
(2);
System.out.println("从ll中移除内容之后:
"+ll);
//移除第一个和最后一个元素
ll.removeFirst();