Java中的集合Collection知识总结.docx
《Java中的集合Collection知识总结.docx》由会员分享,可在线阅读,更多相关《Java中的集合Collection知识总结.docx(14页珍藏版)》请在冰豆网上搜索。
Java中的集合Collection知识总结
Java知识总结
Java中的集合(Collection)
BUGSBUNNY
2011-9-7
Java中的集合(Collection)
Collection(类集)
类集。
一个类集(collection)是一组对象。
类集框架被设计用于适应几个目的:
1、这种框架是高性能的。
对基本类集(动态数组,链接表,树和散列表)的实现是高效率的。
一般很少需要人工去对这些“数据引擎”编写代码(如果有的话)。
2、框架必须允许不同类型的类集以相同的方式和高度互操作方式工作。
3、类集必须是容易扩展和/或修改的。
为了实现这一目标,类集框架被设计成包含一组标准的接口。
对这些接口,提供了几个标准的实现工具(例如LinkedList,HashSet和TreeSet),通常就是这样使用的。
如果你愿意的话,也可以实现你自己的类集。
为了方便起见,创建用于各种特殊目的的实现工具。
一部分工具可以使你自己的类集实现更加容易。
最后,增加了允许将标准数组融合到类集框架中的机制
算法(Algorithms)是类集机制的另一个重要部分。
算法操作类集,它在Collections类中被定义为静态方法。
因此它们可以被所有的类集所利用。
每一个类集类不必实现它自己的方案,算法提供了一个处理类集的标准方法
由类集框架创建的另一项是Iterator接口。
一个迭代程序(iterator)提供了一个多用途的,标准化的方法,用于每次访问类集的一个元素。
因此迭代程序提供了一种枚举类集内容(enumeratingthecontentsofacollection)的方法。
因为每一个类集都实现Iterator,所以通过由Iterator定义的方法,任一类集类的元素都能被访问到。
集合框架中的接口
集合框架中的实现类
除了类集接口之外,类集也使用Comparator,Iterator和ListIterator接口。
•简单地说,Comparator接口定义了两个对象如何比较;Iterator和ListIterator接口枚举类集中的对象。
为了在它们的使用中提供最大的灵活性,类集接口允许对一些方法进行选择。
可选择的方法使得使用者可以更改类集的内容。
支持这些方法的类集被称为可修改的(modifiable)。
不允许修改其内容的类集被称为不可修改的(unmodifiable)。
如果对一个不可修改的类集使用这些方法,将引发一个UnsupportedOperationException异常。
所有内置的类集都是可修改的。
Collection接口是构造类集框架的基础。
它声明所有类集都将拥有的核心方法。
因为所有类集实现Collection,所以熟悉它的方法对于清楚地理解框架是必要的。
其中几种方法可能会引发一个UnsupportedOperationException异常。
正如上面解释的那样,这些发生在当类集不能被修改时。
当一个对象与另一个对象不兼容,例如当企图增加一个不兼容的对象到一个类集中时。
将产生一个ClassCastException异常
调用add()方法可以将对象加入类集。
注意add()带一个Object类型的参数。
因为Object是所有类的超类,所以任何类型的对象可以被存储在一个类集中。
然而原始类型不行。
例如,一个类集不能直接存储类型int,char,double等的值。
当然如果想存储这些对象,也可以使用原始类型包装器。
可以通过调用addAll()方法将一个类集的全部内容增加到另一个类集中。
可以通过调用remove()方法将一个对象删除。
为了删除一组对象,可以调用removeAll()方法。
调用retainAll()方法可以将除了一组指定的元素之外的所有元素删除。
为了清空类集,可以调用clear()方法
通过调用contains()方法,可以确定一个类集是否包含了一个指定的对象。
•为了确定一个类集是否包含了另一个类集的全部元素,可以调用containsAll()方法
•当一个类集是空的时候,可以通过调用isEmpty()方法来予以确认。
•调用size()方法可以获得类集中当前元素的个数
toArray()方法返回一个数组,这个数组包含了存储在调用类集中的元素。
通过在类集和数组之间提供一条路径,可以充分利用这两者的优点
一个更加重要的方法是iterator(),该方法对类集返回一个迭代程序。
当使用一个类集框架时,迭代程序对于成功的编程来说是至关重要的
Collection:
集合层次中的根接口,JDK没有提供这个接口直接的实现类。
•Set:
不能包含重复的元素。
SortedSet是一个按照升序排列元素的Set。
•List:
是一个有序的集合,可以包含重复的元素。
提供了按索引访问的方式。
•Map:
包含了key-value对。
Map不能包含重复的key。
SortedMap是一个按照升序排列key的Map。
List接口
•List接口扩展了Collection并声明存储一系列元素的类集的特性。
使用一个基于零的下标,元素可以通过它们在列表中的位置被插入和访问。
一个列表可以包含重复元素
除了由Collection定义的方法之外,List还定义了一些它自己的方法。
再次注意当类集不能被修改时,其中的几种方法引发UnsupportedOperationException异常。
当一个对象与另一个不兼容,例如当企图将一个不兼容的对象加入一个类集中时,将产生ClassCastException异常
对于由Collection定义的add()和addAll()方法,List增加了方法add(int,Object)和addAll(int,Collection)。
这些方法在指定的下标处插入元素。
由Collection定义的add(Object)和addAll(Collection)的语义也被List改变了,以便它们在列表的尾部增加元素
为了获得在指定位置存储的对象,可以用对象的下标调用get()方法。
为了给类表中的一个元素赋值,可以调用set()方法,指定被改变的对象的下标。
调用indexOf()或lastIndexOf()可以得到一个对象的下标
•通过调用subList()方法,可以获得列表的一个指定了开始下标和结束下标的子列表。
subList()方法使得列表处理十分方便。
Set接口
集合接口定义了一个集合。
它扩展了Collection并说明了不允许复制元素的类集的特性。
因此,如果试图将复制元素加到集合中时,add()方法将返回false。
它本身并没有定义任何附加的方法
SortedSet接口
SortedSet接口扩展了Set并说明了按升序排列的集合的特性。
当没有项包含在调用集合中时,其中的几种方法引发NoSuchElementException异常。
当对象与调用集合中的元素不兼容时,引发ClassCastException异常。
如果试图使用null对象,而集合不允许null时,引发NullPointerException异常
SortedSet定义了几种方法,使得对集合的处理更加方便。
调用first()方法,可以获得集合中的第一个对象。
调用last()方法,可以获得集合中的最后一个元素。
调用subSet()方法,可以获得排序集合的一个指定了第一个和最后一个对象的子集合。
如果需要得到从集合的第一个元素开始的一个子集合,可以使用headSet()方法。
如果需要获得集合尾部的一个子集合,可以使用tailSet()方法。
Collection类
ArrayList
ArrayList:
我们可以将其看作是能够自动增长容量的数组。
•利用ArrayList的toArray()返回一个数组。
•Arrays.asList()返回一个列表。
•迭代器(Iterator)给我们提供了一种通用的方式来访问集合中的元素。
ArrayList类扩展AbstractList并执行List接口。
ArrayList支持可随需要而增长的动态数组。
在Java中,标准数组是定长的。
在数组创建之后,它们不能被加长或缩短,这也就意味着你必须事先知道数组可以容纳多少元素。
但是,你直到运行时才能知道需要多大的数组。
为了解决这个问题,类集框架定义了ArrayList。
本质上,ArrayList是对象引用的一个变长数组。
也就是说,ArrayList能够动态地增加或减小其大小。
数组列表以一个原始大小被创建。
当超过了它的大小,类集自动增大。
当对象被删除后,数组就可以缩小
ArrayList有如下的构造函数
–ArrayList()
–ArrayList(Collectionc)
–ArrayList(intcapacity)
–第一个构造函数建立一个空的数组列表。
–第二个构造函数建立一个数组列表,该数组列表由类集c中的元素初始化。
–第三个构造函数建立一个数组列表,该数组有指定的初始容量(capacity)。
容量是用于存储元素的基本数组的大小。
当元素被追加到数组列表上时,容量会自动增加
使用由toString()方法提供的默认的转换显示类集的内容,toString()方法是从AbstractCollection继承下来的。
尽管它对简短的例子程序来说是足够了,然而很少使用这种方法去显示实际中的类集的内容。
通常编程者会提供自己的输出程序。
尽管当对象被存储在ArrayList对象中时,其容量会自动增加。
仍可以通过调用ensureCapacity()方法来人工地增加ArrayList的容量。
如果事先知道将在当前能够容纳的类集中存储许许多多的项时,你可能会想这样做。
在开始时,通过一次性地增加它的容量,就能避免后面的再分配。
因为再分配是很花时间的,避免不必要的处理可以改善性能
ensureCapacity()方法如下所示
–voidensureCapacity(intcap)
–这里,cap是新的容量
•相反地,如果想要减小在ArrayList对象之下的数组的大小,以便它有正好容纳当前项的大小,可以调用trimToSize()方法。
该方法如下
–voidtrimToSize()
从数组列表(ArrayList)获得数组(Array)
当使用ArrayList时,有时想要获得一个实际的数组,这个数组包含了列表的内容。
可以通过调用方法toArray()来实现它。
下面是几个为什么可能想将类集转换成为数组的原因
–对于特定的操作,可以获得更快的处理时间
–为了给方法传递数组,而方法不必重载去接收类集
–为了将新的基于类集的程序与不认识类集的老程序集成
Arrays.asList()
•返回一个受指定数组支持的固定大小的列表。
(对返回列表的更改会“直写”到数组。
)此方法同Collection.toArray一起,充当了基于数组的API与基于collection的API之间的桥梁
LinkedList类
LinkedList类扩展AbstractSequentialList并执行List接口。
它提供了一个链接列表数据结构。
它具有如下的两个构造函数,说明如下
–LinkedList()
–LinkedList(Collectionc)
–第一个构造函数建立一个空的链接列表。
–第二个构造函数建立一个链接列表,该链接列表由类集c中的元素初始化
除了它继承的方法之外,LinkedList类本身还定义了一些有用的方法,这些方法主要用于操作和访问列表。
使用addFirst()方法可以在列表头增加元素;使用addLast()方法可以在列表的尾部增加元素。
它们的形式如下所示
–voidaddFirst(Objectobj)
–voidaddLast(Objectobj)
–这里,obj是被增加的项
调用getFirst()方法可以获得第一个元素。
调用getLast()方法可以得到最后一个元素。
它们的形式如下所示:
–ObjectgetFirst()
–ObjectgetLast()
•为了删除第一个元素,可以使用removeFirst()方法;为了删除最后一个元素,可以调用removeLast()方法。
它们的形式如下所示
–ObjectremoveFirst()
–ObjectremoveLast()
因为LinkedList实现List接口,调用add(Object)将项目追加到列表的尾部,如同addLast()方法所做的那样。
使用add()方法的add(int,Object)形式,插入项目到指定的位置,如例子程序中调用add(1,“A2”)的举例
–注意如何通过调用get()和set()方法而使得LinkedList中的第三个元素发生了改变。
为了获得一个元素的当前值,通过get()方法传递存储该元素的下标值。
为了对这个下标位置赋一个新值,通过set()方法传递下标和对应的新值
LinkedList是采用双向循环链表实现的。
•利用LinkedList实现栈(stack)、队列(queue)、双向队列(double-endedqueue)。
ArrayList和LinkedList的比较
ArrayList底层采用数组完成,而LinkedList则是以一般的双向链表(double-linkedlist)完成,其内每个对象除了数据本身外,还有两个引用,分别指向前一个元素和后一个元素。
•如果我们经常在List的开始处增加元素,或者在List中进行插入和删除操作,我们应该使用LinkedList,否则的话,使用ArrayList将更加快速。
HashSet类
HashSet扩展AbstractSet并且实现Set接口。
它创建一个类集,该类集使用散列表进行存储。
散列表通过使用称之为散列法的机制来存储信息。
在散列(hashing)中,一个关键字的信息内容被用来确定唯一的一个值,称为散列码(hashcode)。
而散列码被用来当做与关键字相连的数据的存储下标。
关键字到其散列码的转换是自动执行的——你看不到散列码本身。
你的程序代码也不能直接索引散列表。
散列法的优点在于即使对于大的集合,它允许一些基本操作如add(),contains(),remove()和size()方法的运行时间保持不变
散列表:
散列表又称为哈希表。
散列表算法的基本思想是:
以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中的地址。
•当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,所有元素存放到新的散列表中,原先的散列表将被删除。
在Java语言中,通过负载因子(loadfactor)来决定何时对散列表进行再散列。
例如:
如果负载因子是0.75,当散列表中已经有75%的位置已经放满,那么将进行再散列。
•负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。
负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多。
•HashSet类的缺省负载因子是0.75。
下面的构造函数定义为:
–HashSet()
–HashSet(Collectionc)
–HashSet(intcapacity)
–HashSet(intcapacity,floatfillRatio)
第一种形式构造一个默认的散列集合。
•第二种形式用c中的元素初始化散列集合。
•第三种形式用capacity初始化散列集合的容量。
•第四种形式用它的参数初始化散列集合的容量和填充比(也称为加载容量)。
填充比必须介于0.0与1.0之间,它决定在散列集合向上调整大小之前,有多少能被充满。
具体的说,就是当元素的个数大于散列集合容量乘以它的填充比时,散列集合被扩大。
对于没有获得填充比的构造函数,默认使用0.75
HashSet没有定义更多的其他方法。
•重要的是,注意散列集合并没有确保其元素的顺序,因为散列法的处理通常不让自己参与创建排序集合。
如果需要排序存储,另一种类集——TreeSet将是一个更好的选择
TreeSet类
TreeSet为使用树来进行存储的Set接口提供了一个工具,对象按升序存储。
访问和检索是很快的。
在存储了大量的需要进行快速检索的排序信息的情况下,TreeSet是一个很好的选择。
下面的构造函数定义为:
–TreeSet()
–TreeSet(Collectionc)
–TreeSet(Comparatorcomp)
–TreeSet(SortedSetss)
第一种形式构造一个空的树集合,该树集合将根据其元素的自然顺序按升序排序。
•第二种形式构造一个包含了c的元素的树集合。
•第三种形式构造一个空的树集合,它按照由comp指定的比较函数进行排序(比较函数将在后面介绍)。
•第四种形式构造一个包含了ss的元素的树集合
通过迭代函数访问类集
通常希望循环通过类集中的元素。
例如,可能会希望显示每一个元素。
到目前为止,处理这个问题的最简单方法是使用iterator,iterator是一个或者实现Iterator或者实现ListIterator接口的对象。
Iterator可以完成循环通过类集,从而获得或删除元素。
ListIterator扩展Iterator,允许双向遍历列表,并可以修改单元
使用迭代函数
在通过迭代函数访问类集之前,必须得到一个迭代函数。
每一个Collection类都提供一个iterator()函数,该函数返回一个对类集头的迭代函数。
通过使用这个迭代函数对象,可以访问类集中的每一个元素,一次一个元素。
通常,使用迭代函数循环通过类集的内容,步骤如下
–1.通过调用类集的iterator()方法获得对类集头的迭代函数。
–2.建立一个调用hasNext()方法的循环,只要hasNext()返回true,就进行循环迭代。
–3.在循环内部,通过调用next()方法来得到每一个元素
对于执行List的类集,也可以通过调用ListIterator来获得迭代函数。
正如上面解释的那样,列表迭代函数提供了前向或后向访问类集的能力,并可让你修改元素。
否则,ListIterator如同Iterator功能一样
ListIterator只适用于那些实现List接口的类集
将用户定义的类存储于Collection中
为了简单,前面的例子在类集中存储内置的对象,如String或Integer。
当然,类集并没有被限制为只能存储内置的对象。
完全相反的是,类集的能力是它能存储任何类型的对象,包括你所创建的类的对象
处理映射
除了类集,Java2还在java.util中增加了映射。
映射(map)是一个存储关键字和值的关联或者说是关键字/值对的对象。
给定一个关键字,可以得到它的值。
关键字和值都是对象。
关键字必须是唯一的。
但值是可以重复的。
有些映射可以接收null关键字和null值。
而有的则不行
Map接口
Map接口映射唯一关键字到值。
关键字(key)是以后用于检索值的对象。
给定一个关键字和一个值,可以存储这个值到一个Map对象中。
当这个值被存储以后,就可以使用它的关键字来检索它。
当调用的映射中没有项存在时,其中的几种方法会引发一个NoSuchElementException异常。
而当对象与映射中的元素不兼容时,引发一个ClassCastException异常。
如果试图使用映射不允许使用的null对象时,则引发一个NullPointerException异常。
当试图改变一个不允许修改的映射时,则引发一个UnsupportedOperationException异常
映射循环使用两个基本操作:
get()和put()。
使用put()方法可以将一个指定了关键字和值的值加入映射。
为了得到值,可以通过将关键字作为参数来调用get()方法。
调用返回该值。
•映射不是类集,但可以获得映射的类集“视图”。
为了实现这种功能,可以使用entrySet()方法,它返回一个包含了映射中元素的集合(Set)。
为了得到关键字的类集“视图”,可以使用keySet()方法。
为了得到值的类集“视图”,可以使用values()方法。
类集“视图”是将映射集成到类集框架内的手段
SortedMap接口
SortedMap接口扩展了Map,它确保了各项按关键字升序排序。
当调用映射中没有的项时,其中的几种方法引发一个NoSuchElementException异常。
当对象与映射中的元素不兼容时,则引发一个ClassCastException异常。
当试图使用映射不允许使用的null对象时,则引发一个NullPointerException异常
排序映射允许对子映射(换句话说,就是映射的子集)进行高效的处理。
使用headMap(),tailMap()或subMap()方法可以获得子映射。
调用firstKey()方法可以获得集合的第一个关键字。
而调用lastKey()方法可以获得集合的最后一个关键字
Map.Entry接口
Map.Entry接口使得可以操作映射的输入。
回想由Map接口说明的entrySet()方法,调用该方法返回一个包含映射输入的集合(Set)。
这些集合元素的每一个都是一个Map.Entry对象
HashMap类
HashMap类使用散列表实现Map接口。
这允许一些基本操作如get()和put()的运行时间保持恒定,即便对大型集合,也是这样的
•下面的构造函数定义为:
–HashMap()
–HashMap(Mapm)
–HashMap(intcapacity)
–HashMap(intcapacity,floatfillRatio)
第一种形式构造一个默认的散列映射。
•第二种形式用m的元素初始化散列映射。
•第三种形式将散列映射的容量初始化为capacity。
•第四种形式用它的参数同时初始化散列映射的容量和填充比。
容量和填充比的含义与前面介绍的HashSet中的容量和填充比相同。
HashMap实现Map并扩展AbstractMap。
它本身并没有增加任何新的方法
•应该注意的是散列映射并不保证它的元素的顺序。
因此,元素加入散列映射的顺序并不一定是它们被迭代函数读出的顺序
TreeMap类
TreeMap类通过使用树实现Map接口。
TreeMap提供了按排序顺序存储关键字/值对的有效手段,同时允许快速检索。
应该注意的是,不像散列映射,树映射保证它的元素按照关键字升序排序
下面的TreeMap构造函数定义为:
–TreeMap()
–TreeMap(Comparatorcomp)
–TreeMap(Mapm)
–TreeMap(SortedMapsm)
第一种形式构造一个空树的映射,该映射使用其关键字的自然顺序来排序。
•第二种形式构造一个空的基于树的映射,该映射通过使用Comparatorcomp来排序(比较函数Comparators将在后面进行讨论)。
•第三种形式用从m的输入初始化树映射,该映射使用关键字的自然顺序来排序。
•第四种形式用从sm的输入来初始化一个树映射,该映射将按与sm相同的顺序来排序
TreeMap实现SortedMap并且扩展AbstractMap。
而它本身并没有另外定义其他方法
比较函数
TreeSet和TreeMap都按排序顺序存储元素。
然而,精确定义采用何种“排序顺序”的是比较函数。
通常在默认的情况下,这些类通过使用被Java称之为“自然顺序”的顺序存储它们的元素,而这种顺序通常也是你所需要的(A在B的前面,1在2的前面,等等)。
如果需要用不同的方法对元素进行排序,可以在构造集合或映射时,指定一个Comparator对象。
这样做为你提供了一种精确控制如何将元素储存到排序类集和映射中的能力