Java中的集合、枚举、泛型【上】

来源:互联网 发布:c语言精品课程网站 编辑:程序博客网 时间:2024/06/05 11:39

http://blog.csdn.net/silentbalanceyh/article/details/4586611

(本章主要讲解Java里面会遇到的所有集合类以及相关用法,还有JDK1.5里面出来的一些关于集合和算法的新内容,主要是方便我们在开发过程中适当地注意选择,而且本章的内容相对前边章节比较少,但是代码量比较大,但是大部分内容都是个人的一些总结。当然这一个章节会涉及到JDK本身提供的一些数据结构的相关内容,以及最基本几个数据结构的相关性能分析。本来是打算先写Java的IO相关的内容,刚好最近有个项目需要经常性使用到Java里面的东西,所以先提供这一个章节给大家。很多人来Email说Java的基础数据类型一个章节的内容太少了,这点我必须说一句抱歉,因为基础数据类型是我第一次写,后来才想到使用后边这样的方式来写BLOG的内容,基础章节的东西我后边会提供专程的章节来讲解控制流程、以及各种关键字的清单。该系列教程的主要目的是为了整理一份个人使用的开发文档,如果有人转载,请来Email告知,谢谢!在整个过程里面如果发现有笔误的地方希望来Email提点:silentbalanceyh@126.com)

本章目录
1.基本概念
2.常用集合——列表、队列、栈
3.常用集合——Set集合、哈希表
4.泛型、枚举

1.基本概念
  一般情况下,在系统开发的时候经常会遇到针对大量的数据进行处理,在处理同类型数据的某些集合的时候,就会使用到一定的集合类的数据结构。在Java语言里面,集合类一般都位于java.util包里面,该包里面的集合类基本提供了我们再开发过程中常用的数据结构,直接使用起来基本可以满足我们的开发要求。JDK在设计的时候针对常用的数据结构和算法做了一些规范(接口)和实现(实现接口的类),所有抽象出来的数据结构和算法统称为Java集合框架(Java Collection Framework),所以我们在使用的时候就不需要考虑数据结构和算法实现细节,只需要使用这些类创建相关的对象出来,然后直接应用就可以了。
  i.集合的数学背景:
  常见的用法里面,集合(Collection)和数学直观的集(Set)是一个概念。集是一个唯一项组,也就是说组中没有重复项,一般情况下,在我们书写数学集合的时候不会遇到重复项;但是在Java集合框架里面,进行了更加严格的定义,针对这种概念的集合,使用Set接口以及许多实现了该接口的Set类来进行。我们在很早的数学课的时候就学过“交集”和“并集”的概念:

  根据上图可以理清楚对应的集合之间的关系,提供一些简单的集合的实例:
  • Java语言的关键字:{"import","final","static","public",...}
  • 数据库返回结果的记录集
  • 非负整数:{0,1,2,...}
  • 空集:{}【该符号为非数学符号】

  在数学定义里面,集的基本特性如下:

  • 集内只包含每项的一个实例
  • 集可以是有限的,也可以是无限的
  • 集可以定义抽象概念
  数学里面映射是一种特殊的集,一般情况称为对集,每个对表示一个元素到另一个元素的映射,在计算机领域里面,映射的例子有:
  • IP地址到域名(DNS)的映射
  • 关键在到数据库记录的映射
  • 2进制到10进制的转换的映射
  在Java集合框架里面,有严格的SetListCollectionMap的正规定义。
  ii.基本结构:
  Java集合框架(Java Collection Framework)简称JCF,下边均用JCF指代,根据整个Java语言里面的集合结构,先看一张网上摘录的图,里面提供了JCF的整体快照:

  上图来自:http://www.blogjava.net/EvanLiu/archive/2007/11/12/159884.html
  在Java里面,JCF定义了几种标准的集合接口:Collection接口、List接口、Set接口、Map接口,将上边的快照简化就变化称了下边这种简化结构:

  需要针对上边的图做理解的是:
  • Collection接口是一组允许重复的对象
  • Set接口继承Collection对象,但是不允许重复,而且无序
  • List接口继承Collection对象,但是允许重复,且有序
  • Map接口既不继承Set也不继承Collection
  关于它们的特性可以用一个思维导图来归纳


  iii.Collection接口、List接口、Set接口、Map接口【顶层接口】
  1)Collection接口概述:
  JCF中的根接口,Collection表示一组对象,这些对象也称为Collection的元素,有些Collection允许有重复元素,有些Collection不允许有重复元素,有些Collection是有序的,有些Collection是无序的。Java里面没有该接口的直接实现,也就是说没有任何一个具体累实现了该接口。开发过程可以自己去开发实现该接口的类,所有实现该接口的类应该提供两个标准的构造方法:[1]一个是无参数的构造方法,用于创建空集合;[2]一个则是带有Collection类型的单参数构造方法,用于创建某个与其元素相同的Collection,实际上后者可以理解为一个集合的复制过程,通过该种构造方法生成某个集合的等效的Collection。但是这种实现方式没有任何Java规范的硬性要求,仅仅是一个约定俗成。
  该接口中包含了一定的“破坏性”方法,是指可修改其操作的Collection的相关方法,如果Collection不支持该操作,则指定这些方法抛出一个UnsupportOperationException,若是这样的情况在调用Collection无效的时候,这些破坏性方法有可能会抛出该异常,这种操作没有绝对抛出异常的说法。一些Collection的实现对它们可能包含的元素有所限制,比如禁止null元素,或者说有些实现类针对某些集合的添加元素有类型限制,一旦操作失败有可能会抛出NullPointerException异常以及ClassCastException,但是有时候操作起来并不抛出异常,也仅仅是返回一个false。
  Collection的接口的定义:
public interface Collection<E> extends Iterable<E>【E代表Collection内的元素类型】
  根据该集合的定义可以知道,Java里面的Collection接口从JDK 1.5开始过后就使用了带泛型的定义:Collection<E>,而且它有一个父接口:Iterable<E>,这里简单介绍一下Iterable<E>接口:
该接口允许对象使用“foreach”语句,这种语句在java里面使用下边格式进行:
forString item : list){}
  Iterable<E>接口仅仅提供一个方法iterator(),该方法返回一个迭代器,【Iterable<E>接口是在1.5的版本中引入的】
public interface Iterable<T>
  Collection的方法定义列表:
boolean add(e) throws UnsupportedOperationException,ClassCastException,NullPointerException,IllegalArgumentException,IllegalStateException
——确保该Collection包含该指定元素、若该调用使得Collection发生了更改返回为true,没有修改成功返回为false
boolean addAll(Collection<? extends E> c) throws UnsupportedOperationException,ClassCastException,NullPointerException,IllegalArgumentException,IllegalStateException
——将指定Collection中的所有元素都添加到该Collection中
void clear() throws UnsupportedOperationException
——清除该Collection中的所有元素
boolean contains(Object o) throws ClassCastException,NullPointerException
——如果该Collection里面包含了指定的元素,则返回true
boolean containsAll(Collection<?> c) throws ClassCastException,NullPointerException
——如果该Collection里面包含了指定Collection中的元素,则返回为true
boolean equals(Object o)
——比较此Collection是否与指定的对象相等
int hashCode()
——返回此Collection的hashCode值
boolean isEmpty()
——如果此Collection不包含任何元素,该方法返回为true
Iterator<E> iterator()
——返回该Collection上的一个迭代器
boolean remove(Object o) throws ClassCastException,NullPointerException,UnsupportedOperationException
——从该Collection中移除指定的单个实例,如果存在的话
boolean removeAll(Collection<?> c) throws ClassCastException,NullPointerException,UnsupportedOperationException
——移除此Collection中那些也包含在指定Collection中的所有元素
boolean retainAll(Collection<?> c) throws ClassCastException,NullPointerException,UnsupportedOperationException
——仅保留此Collection中那些也包含在指定元素里的元素,类似一个元素合并操作
int size()
——返回包含在Collection中的元素个数
Object[] toArray()
——返回包含此Collection中所有元素的数组
<T> T[] toArray(T[] a) throws ArrayStoreException,NullPointerException
——返回包含此Collection中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同

  2)List接口概述:
  List接口称为有序的Collection(也可以称为序列),调用该接口可以针对列表中的元素插入的位置进行精确的控制,调用者也可以使用整数索引来访问对应的元素,并且进行搜索相关操作,该列表允许重复的元素。该接口在父接口Collection的方法iterator、add、remove、equals和hashCode方法的协定上加了一部分约定,超过了Collection本身的方法约定。List接口的元素访问比起Collection可能复杂很多:
  • 该接口提供了4种对列表元素进行定位访问的方法
  • 该接口提供了特殊的迭代器ListIterator,除了允许正常访问以外,还允许元素的插入和替换,以及双向访问【不仅仅提供next操作】
  • 该接口提供了两种方法搜索指定对象
  • 该接口提供了两种在列表的任意位置高效插入或者移除多个元素方法
  【*:在这个列表上,equals和hashCode的实现不是定义良好的】
  List接口的定义:
public interface List<E> extends Collection<E>【E代表List内的元素的类型】
  List的方法定义列表【这里不重复父类Collection的方法定义】:
void add(int index,element) throws UnsupportedOperationException,ClassCastException,NullPointerException,IllegalArgumentException,IndexOutOfBoundsException
——在列表的指定位置插入指定元素
boolean addAll(int index,Collection<extends E> c) throws UnsupportedOperationException,ClassCastException,NullPointerException,IllegalArgumentException,IndexOutOfBoundsException
——将Collection里面所有的元素都插入到列表里面指定的位置,而插入进去的元素顺序按照迭代器从Collection里面迭代出来的顺序插入
get(int index) throws IndexOutOfBoundsException
——返回列表中指定索引位置的元素
int indexOf(Object o) throws ClassCastException,NullPointerException
——返回该列表中第一次出现该元素的索引,如果列表不包含此元素,就返回-1
int lastIndexOf(Object o) throws ClassCastException,NullPointerException
——返回此列表中最后出现指定元素的索引,如果该列表不包含此元素,就返回-1
ListIterator<E> listIterator() 
——返回此列表元素的列表迭代器(按照适当的顺序)
ListIterator<E> listIterator(int index) throws IndexOutOfBoundsException
——返回列表中元素的列表迭代器(按照适当的顺序),从列表指定的位置开始
set(int index,element) throws UnsupportedOperationException,ClassCastException,NullPointerException,IllegalArgumentException,IndexOutOfBoundsException
——用指定元素替换掉该索引位置本来的元素
List<E> subList(int fromIndex,int toIndex) throws IndexOutOfBoundsException
——返回列表中指定的fromIndex(包括)和toIndex(不包括)之间部分的一个子列表
  【*:需要注意的是,因为List接口是有序的接口,它继承的父接口Collection里面的所有操作都是针对有序和无序两种,Collection本身没有头部和尾部的概念,而List本身是存在头部和尾部的概念的,比如调用add方法,针对Collection而言仅仅是把元素添加进入该集合,但是针对List而言,默认情况是将元素添加到该List的尾部,也就是说List有一个很大的特点就是它是有序的,而且以List结构存储的元素结构在List中存在一个“位置”的概念】

  3)Set接口概述:
  一个不包含重复的无序的Collection,Set不包含两个相同的元素(使用equals方法比较返回为true),并且最多包含一个null元素,该结构是按照集合的数学定义来实现的,而且Set的特殊情况在于该集合不允许包含其自身作为元素。
  【*:如果将可变对象作为Set集合的元素,那么必须极其小心,如果对象是Set中的某个元素,以一种影响equals比较的方式改变了对象的值的话,那么这种情况下Set集合的值会变得不确定。】
  Set接口的定义:
public interface Set<E> extends Collection<E>【E代表Set集合内的元素的类型】
  Set的方法列表和Collection是完全一样的,所以这里没有列出来,需要注意的是:在父接口所有的构造方法、add、equals以及hashCode等方法的协定上,Set接口还加入了其他规定,这些规定超出了从Collection接口继承的相关内容。Set集合的主要特点就是无序、不可重复,其实Set可能更容易理解,因为Set和集合的数学定义是一致的。

  4)Map接口概述:
  Map接口一般我们称为映射,它有一个很特殊的特征:一个映射不能包含重复的键,而且每个键只能映射到一个值,该接口定义如下:
public interface Map<K,V>【K代表此映射所维护的键的类型,V代表此映射对应的值的类型】
  Map接口提供了三种可以访问的试图集合:键集、值集、键-值关系实体集的形式查看某个映射的内容。该集合的顺序就为迭代器在该集合上迭代出来的元素的顺序,而且有些实现了该接口的类是可以保证顺序的,就是Map接口下边的实现有些是有序的,有些是无序的。
  【*:如果一个对象可变的话,它作为该Map的键的时候需要小心,这个后边深入的时候讲】
  Map接口的方法定义列表:
void clear() throws UnsupportedOperationException
——从此映射中移除所有的映射关系
boolean containsKey(Object key) throws ClassCastException,NullPointerException
——如果此映射包含指定键的映射关系返回true
boolean containsValue(Object value) throws ClassCastException,NullPointerException
——如果此映射将一个或者多个键映射到指定的值,则返回true
Set<Map.Entry<K,V>> entrySet()
——返回该映射中包含的映射关系实体集的一个视图[Map.Entry<K,V>为Map接口内的嵌套类]【键-值关系实体集视图】
boolean equals(Object o)
——比较指定的对象与此映射是否相等
get(Object key) throws ClassCastException,NullPointerException
——返回指定键对应的值;如果此映射不包含该映射关系,直接返回null
int hashCode()
——返回此映射的哈希吗值
boolean isEmpty()
——如果此映射中未包含键值关系映射,则返回true
Set<K> keySet()
——返回此映射中包含键的Set视图【键集视图】
put(key,value) throws UnsupportedOperationException,ClassCastException,NullPointerException,IllegalArgumentException
——将指定的值与此映射中的指定键关联
void putAll(Map<extends Kextends V> m) throws UnsupportedOperationException,ClassCastException,NullPointerException,IllegalArgumentException
——从指定映射中将所有映射关系复制到该映射里面
remove(Object key) throws UnsupportedOperationException,ClassCastException,NullPointerException
——如果存在一个键的映射关系,则将其从此映射中移除
int size()
——返回此映射中的键值映射关系数
Collection<V> values()
——返回该映射中的值的Collection视图【值集视图】
  Map接口中的嵌套接口Map.Entry<K,V>
  Map.Entry接口的定义:
public static interface Map.Entry<K,V>
  Map.Entry方法定义列表:
boolean equals(Object o)
——比较指定对象与该项的相等性
getKey() throws IllegalStateException
——返回与此项对应的键
getValue() throws IllegalStateException
——返回与此项对应的值
int hashCode() 
——返回此映射项的哈希吗值
setValue(value) throws UnsupportedOperationException,ClassCastException,NullPointerException,IllegalArgumentException,IllegalStateException
——用指定的值替换与此项对应的值

  iv其他java.util包里面的接口【*:该部分内容可能相对复杂,初学者暂时可以略过】
  1)Queue<E>接口(1.5):父接口[Collection<E>]
public interface Queue<E> extends Collection<E>
  该接口针对Collection接口做了一定的改动,是一种带特殊数据结构的接口,它不仅仅提供了Collection的普通操作方法,而且针对每种方法都提供了不同的形式:
  [1]操作失败的时候,抛出异常;
  [2]操作失败的时候,返回一个特殊值(比如null或者false)

抛出异常返回特殊值插入add(e)offer(e)移除remove()poll()检查element()peek()
  一般情况下,队列都是以FIFO(先进先出)的方式排列元素,不过有两种数据接口是例外的:优先级队列、LIFO队列(堆栈)。前者会根据比较器或元素的自然顺序进行排序,后者按照LIFO(后进先出)的方式对元素进行排序。不论使用哪种排序方式,队列的头都是调用remove()或poll()两种方法来进行元素的移除。在FIFO队列中,所有的元素都会插入到队列的队尾,其他种类的队列可能使用不同的元素放置规则,每个Queue实现必须指定该顺序属性。虽然提供了两种不同的插入、移除、检查方法,但是这些方法有一定的异同点:
  • offer方法可以插入一个元素,否则就返回false,该方法有别于Collection.add方法的地方为:add方法只能通过抛出未经检查的异常使添加元素失败,offer方法设计用于正常的失败情况,不会抛出异常
  • remove()和poll()方法主要做两个事情:[1]移除队列里面选择的元素;[2]返回该队列的头;至于该元素的顺序是根据不同的实现策略进行操作的,这两个方法唯一的不同点:当一个队列为空的时候,remove()方法抛出一个异常,而poll直接返回null。【*:这里的队列为空指代的是该队列里面不包含此元素,并不是说队列本身的引用为空,这一点小心】
  • element()和peek():直接返回队列的头元素,但是不移除
  【*:Queue实现通常不允许插入null元素,尽管某些实现没有禁止插入null。这里需要考虑到的一个问题在于,如果队列里面插入了null元素,那么在返回的时候和上边第二条可能会存在二义性,因为null的返回值在作用于poll方法的时候也代表该队列里面什么元素都不包含,如果本身存在一个null元素,在调用该方法的时候到底是某个元素为null还是这个集合里面已经什么元素都不包括在这里是无法判断的,所以一般情况下不允许将null插入到Queue的实现里面,即使有些类可以插入null元素。】

  2)Deque<E>接口(1.6):父接口[Queue<E>]
public interface Deque<E> extends Queue<E>
  一个线性的Collection,支持在两端进行插入和移除操作,又称为双端队列[double ended queue的缩写],大所属Deque实现对于它们能够包含的元素没有特殊的限制,但是该接口既支持持有容量限制的双端队列,也支持没有固定大小的双端队列。该接口定义在双端队列两端访问的方法包括:插入、移除、检查,和Queue一样,提供了两种不同的形式:

第一个元素(头部)最后一个元素(尾部)

抛出异常特殊值抛出异常特殊值插入addFirst(e)offerFirst(e)addLast(e)offerLast(e)移除removeFirst()pollFirst()removeLast()pollLast()检查getFirst()peekFirst()getLast()peekLast()
  该接口是从Queue<E>继承过来的,下边是和Queue<E>等效的方法
Queue方法Deque方法【等效】add(e)addLast(e)offer(e)offerLast(e)remove()removeFirst()poll()pollFirst()element()getFirst()peek()peekFirst()
  在老版本中存在遗留的Stack类,主要适用于LIFO堆栈的数据结构,而使用Deque<E>同样包含了与之等效的方法
堆栈方法Deque方法【等效】push(e)addFirst(e)pop()removeFirst()peek()peekFirst()
  【*:该接口有别于List接口的地方在于,不能通过索引访问集合里面的相关元素】

  3)SortedMap<K,V>接口(1.2):父接口[Map<K,V>]
public interface SortedMap<K,V> extends Map<K,V>
  进一步提供了键的排序的映射,该映射的键是按照自然顺序进行排序的,或者根据通常在创建有序映射的时候提供的Comparator进行排序,对有序的Collection视图进行迭代操作的时候该顺序就会反映出来。
  【*:插入有序的映射的所有键必须实现了Comparable接口(或者被指定的比较器接受),另外所有的这些键都是可相互比较的,真对有序映射中的任意两个键k1和k2应该在调用k1.compareTo(k2)和Comparator.compare(k1,k2)的时候都不应该抛出ClassCastException异常。注意,如果有序映射要正确实现 Map 接口,则有序映射所维持的顺序(无论是否提供了明确的比较器)都必须与 equals 一致。(有关与 equals 一致 的精确定义,请参阅 Comparable 接口或 Comparator 接口)。这是因为Map 接口是按照 equals 操作定义的,但有序映射使用它的 compareTo(或 compare)方法对所有键进行比较,因此从有序映射的角度来看,此方法认为相等的两个键就是相等的。即使顺序与 equals 不一致,树映射的行为仍然 定义良好的,只不过没有遵守 Map 接口的常规协定。
  根据Map接口的相关定义,所有该接口的映射实现类应该提供四个标准的构造方法:
  • 无参数构造方法,它创建一个空的有序映射,按照键的自然排序进行排序。
  • 带有一个Comparator类型参数的构造方法,它同样创建一个空的有序映射,根据指定的比较器进行排序。
  • 带有一个Map类型参数的构造方法,创建一个新的有序映射,按照键的自然顺序进行排序,其映射关系和本身的Map参数是一致的,不一样的是键的排序方式,因为有可能Map本身的键不是按照自然顺序排列的。
  • 带有一个SortedMap类型参数的构造方法,创建一个新的有序映射,该键值关系和传入参数一致,而且该键的排序方式和传入的SortedMap的键的排序方式也是一致的。
  SortedMap<K,V>接口的方法列表【父接口Map的方法这里不列举了】:
Comparator<super K> comparator()
——返回对此映射中的键进行排序的比较器,如果此映射使用键的自然排序,就返回null
firstKey() throws NoSuchElementException
——返回此映射中当前的第一个键
SortedMap<K,V> headMap(toKey) throws ClassCastException,NullPointerException,IllegalArgumentException
——返回此映射里面的部分映射,也就是返回一个子映射,从第一个键值到toKey(不包含)的所有映射的一个返回视图
lastKey() throws NoSuchElementException
——返回映射中的最后一个键
SortedMap<K,V> subMap(fromKey,toKey) throws ClassCastException,NullPointerException,IllegalArgumentException
——返回该映射的一个子映射的视图,返回的键值为fromKey(包括)toKey(不包括)的一个子映射的视图集合
SortedMap<K,V> tailMap(fromKey) throws ClassCastException,NullPointerException,IllegalArgumentException
——返回该映射的一个子映射视图,返回的键值从fromKey(包括)到最后一个键

  4)SortedSet<K,V>接口(1.2):父接口[Map<K,V>]
public interface SortedSet<E> extends Set<E>
  进一步提供了元素总体排序的集合,该集合的每个元素是按照自然顺序进行排序的,或者根据通常在创建有序集合的时候提供的Comparator进行排序,对有序的Collection视图进行迭代操作的时候该顺序就会反映出来。
  【*:插入有序的集合的所有元素必须实现了Comparable接口(或者被指定的比较器接受),另外所有的这些元素都是可相互比较的,真对有序集合中的任意两个元素e1和e2应该在调用e1.compareTo(e2)和Comparator.compare(e1,e2)的时候都不应该抛出ClassCastException异常。注意,如果有序集合要正确实现 Set 接口,则有序集合所维持的顺序(无论是否提供了明确的比较器)都必须与 equals 一致。(有关与 equals 一致 的精确定义,请参阅 Comparable 接口或 Comparator 接口。)这是因为Set 接口是按照 equals 操作定义的,但有序集合使用它的 compareTo(或 compare)方法对所有元素进行比较,因此从有序集合的角度来看,此方法认为相等的两个元素就是相等的。即使顺序与 equals 不一致,有序集合的行为仍然是定义良好的,只不过没有遵守 Set 接口的常规协定。
  根据Set接口的相关定义,所有该接口的集合实现类应该提供四个标准的构造方法:
  • 无参数构造方法,它创建一个空的有序集合,按照元素的自然排序进行排序。
  • 带有一个Comparator类型参数的构造方法,它同样创建一个空的有序集合,根据指定的比较器进行排序。
  • 带有一个Collection类型参数的构造方法,创建一个新的有序集合,按照元素的自然顺序进行排序。
  • 带有一个SortedSet类型参数的构造方法,创建一个新的有序集合,该元素的排序方式和传入的SortedSet中的元素的排序方式一致。

  5)NavigableMap<K,V>接口(1.6):父接口[SortedMap<K,V>]
public interface NavigableMap<K,V> extends SortedMap<K,V>
  该接口是1.6版本里面新出来的接口,扩展自SortedMap<K,V>,具有针对给定搜索目标放回最接近匹配项的导航方法。
  • 方法lowerEntry、floorEntry、ceilingEntry、higherEntry分别返回小于、小于等于、大于等于、大于给定键的键关联的Map.Entry对象,若不存在的时候返回null
  • 方法lowerKey、floorKey、ceilingKey、higerKey分别返回小于、小于等于、大于等于、大于的关联键
  【*:以上的方法都是为了查找而设计的,并不是为了遍历而设计的方法】
  不仅仅如此,还可以按照键的升序或降序访问和遍历NavigableMap,descendingMap方法返回映射的一个视图,该视图表示的所有关系方法和方向方法都是逆向操作的
  NavigableMap<K,V>接口的方法列表【父接口的方法这里不列举】
Map.Entry<K,V> ceilingEntry(key) throws ClassCastException,NullPointerException
——返回一个键值映射关系,它与大于等于给定键的最小键关联
ceilingKey(key) throws ClassCastException,NullPointerException
——返回大于等于给定键的最小值,若不存在返回null
NavigableSet<K> descendingKeySet()
——返回该映射中包含的键的逆序NavigableSet视图
NavigableMap<K,V> descendingMap()
——返回该映射中包含的映射关系的逆序视图
Map.Entry<K,V> firstEntry()
——返回一个与此映射中的最小关联的键值映射关系
Map.Entry<K,V> floorEntry(key) throws ClassCastException,NullPointerException
——返回一个键值映射关系,它与小于给定键的最大键关联
floorKey(key) throws ClassCastException,NullPointerException
——返回小于等于给定键的最大键
NavigableMap<K,V> headMap(toKey,boolean inclusive) throws ClassCastException,NullPointerException
——返回此映射的部分视图,其键小于(或等于,如果inclusive为true)toKey
Map.Entry<K,V> higherEntry(key) throws ClassCastException,NullPointerException
——返回一个键值映射关系,它严格大于给定键的最小键关联
higherKey(key) throws ClassCastException,NullPointerException
——返回严格大于给定键的最小键
Map.Entry<K,V> lastEntry()
——返回与此映射中的最大键关联的键值映射关系
Map.Entry<K,V> lowerEntry(key) throws ClassCastException,NullPointerException
——返回一个键值映射关系,它与严格小于给定键的最大键关联
lowerKey(key) throws ClassCastException,NullPointerException
——返回严格小于给定键的最大键
NavigableSet<K> navigableKeySet()
——返回此映射中所包含的键的NavigableSet视图
Map.Entry<K,V> pollFirstEntry()
——移除并且返回此映射中的最小键关联的键值映射关系
Map.Entry<K,V> pollLastEntry()
——移除并返回与此映射中最大键关联的键值映射关系
NavigableMap<K,V> subMap(fromKey,boolean fromInclusive,toKey,boolean toInclusive) throws ClassCastException,NullPointerException,IllegalArgumentException
——返回此映射的部分视图,其键的范围从fromKey到toKey
NavigableMap<K,V> tailMap(fromKey,boolean inclusive) throws ClassCastException,NullPointerException,IllegalArgumentException
——返回此映射的部分映射,其键大于(或等于,若inclusive为true)fromKey
  以上的方法如果不存在某个元素的时候,直接返回为null,下边的类的方法也类似

  6)NavigableSet<E>接口(1.6):父接口[SortedSet<E>]
public interface NavigableSet<E> extends SortedSet<E>
  该接口具有为给定搜索目标报告最接近匹配项的导航方法,方法lower、floor、ceiling和higher分别返回小于、小于等于、大于等于、大于给定元素的元素,如果不存在返回为null。
  NavigableSet<E>接口的方法列表【父接口方法这里不列举】
ceiling(e) throws ClassCastException,NullPointerException
——返回该集合中大于等于给定元素的最小元素
Iterator<E> descendingIterator()
——以降序返回此集合的元素上进行迭代的迭代器
NavigableSet<E> descendingSet()
——返回此集合中所包含元素的逆序视图
floor(e) throws ClassCastException,NullPointerException
——返回此集合中小于等于给定元素的最大元素
NavigableSet<E> headSet(toElement,boolean inclusive) throws ClassCastException,NullPointerException,IllegalArgumentException
——返回此集合的部分视图,其元素小于(或等于,如果inclusive为true)toElement
higher(e) throws ClassCastException,NullPointerException
——返回此集合中严格大于给定元素的最小元素
Iterator<E> iterator()
——升序返回在此集合的元素上进行迭代的迭代器
lower(e) throws ClassCastException,NullPointerException
——返回此集合中秧歌小于给定元素的最小元素
pollFirst()
——获取并移除第一个(最低)元素,若集合为空,返回null
pollLast()
——获取并移除最后一个(最高)元素,若集合为空,返回null
NavigableSet<E> subSet(fromElement,boolean fromInclusive,toElement,boolean toInclusive) throws ClassCastException,NullPointerException,IllegalArgumentException
——返回该集合的部分试图,元素范围从fromElement到toElement,如果fromInclusive和toInclusive都为true的时候就包括该边界元素
NavigableSet<E> tailSet(fromElement,boolean inclusive) throws ClassCastException,NullPointerException,IllegalArgumentException
——返回集合的部分视图,其元素大于(或等于,如果inclusive为true)fromElement

  v.对象的比较(Comparator和Comparable)
  1)Comparator(1.2)的使用:
  该接口的方法:
int compare(o1,o2) throws ClassCastException
boolean equals(Object obj)
  这里主要解释的是compare(T o1,T o2)方法,这个方法主要是用来比较两个参数,根据第一个参数小于、等于或者大于第二个参数分别返回负整数、零或正整数。
package org.susan.java.compara;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
 *关于Comparator接口的使用方法
 **/
class Employee{
    private String name;
    private int age;
    private int salary;
    public Employee(String name,int age,int salary){
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getSalary() {
        return salary;
    }
    public void setSalary(int salary) {
        this.salary = salary;
    }
}

class AgeComparator implements Comparator<Employee>{
    public int compare(Employee o1,Employee o2){
        Employee eOp1 = (Employee)o1;
        Employee eOp2 = (Employee)o2;
        return (eOp1.getAge())-(eOp2.getAge());
    }
}

class NameComparator implements Comparator<Employee>{
    public int compare(Employee o1,Employee o2){
        Employee eOp1 = (Employee)o1;
        Employee eOp2 = (Employee)o2;
        return eOp1.getName().compareTo(eOp2.getName());
    }
}

class SalaryComparator implements Comparator<Employee>{
    public int compare(Employee o1,Employee o2){
        Employee eOp1 = (Employee)o1;
        Employee eOp2 = (Employee)o2;
        return eOp1.getSalary() - (eOp2.getSalary());
    }
}

public class SampleComparator {
    public static void display(List<Employee> employees){
        for(Employee e:employees){
            System.out.println("Name=" + e.getName() + " Age=" + e.getAge() + " Salary=" + e.getSalary());
        }
        System.out.println();
    }
    public static void main(String args[]){
        List<Employee> employees = new ArrayList<Employee>();
        employees.add(new Employee("Andy",21,2000));
        employees.add(new Employee("Felix",21,3000));
        employees.add(new Employee("Bill",35,20000));
        employees.add(new Employee("Helen",21,10000));
        employees.add(new Employee("Cindy",28,8000));
        employees.add(new Employee("Douglas",25,5000));
        //按名称排序
        Collections.sort(employees,new NameComparator());
        display(employees);
        //按年龄排序
        Collections.sort(employees,new AgeComparator());
        display(employees);
        //按薪水排序
        Collections.sort(employees,new SalaryComparator());
        display(employees);
    }
}
  下边是使用该接口的程序输出:
Name=Andy Age=21 Salary=2000
Name=Bill Age=35 Salary=20000
Name=Cindy Age=28 Salary=8000
Name=Douglas Age=25 Salary=5000
Name=Felix Age=21 Salary=3000
Name=Helen Age=21 Salary=10000

Name=Andy Age=21 Salary=2000
Name=Felix Age=21 Salary=3000
Name=Helen Age=21 Salary=10000
Name=Douglas Age=25 Salary=5000
Name=Cindy Age=28 Salary=8000
Name=Bill Age=35 Salary=20000

Name=Andy Age=21 Salary=2000
Name=Felix Age=21 Salary=3000
Name=Douglas Age=25 Salary=5000
Name=Cindy Age=28 Salary=8000
Name=Helen Age=21 Salary=10000
Name=Bill Age=35 Salary=20000
  2)Comparable接口(1.2)的使用:
  该接口里面的方法:
int comparaTo(o)
  比较此对象和指定对象的顺序,如果该对象小于、等于或者大于指定对象,则分别返回负整数、零和正整数
package org.susan.java.compara;

import java.util.Arrays;
/**
 *关于Comparable接口的使用
 **/
public class UserCompara implements Comparable<UserCompara>{
    private String id;
    private int age;
    public UserCompara(String id,int age){
        this.id = id;
        this.age = age;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int compareTo(UserCompara o){
        return this.age - ((UserCompara)o).getAge();
    }
    public static void main(String args[]){
        UserCompara[] users = new UserCompara[]{ 
            new UserCompara("a",30),
            new UserCompara("b",20),
            new UserCompara("c",10)};
        Arrays.sort(users);
        forint i = 0; i < users.length; i++ ){
            UserCompara user = users[i];
            System.out.println("ID=" + user.getId() + " Age=" + user.getAge());
        }
    }
}
  上边这段代码的输出为:
ID=c Age=10
ID=b Age=20
ID=a Age=30
  3)Comparator和Comparable的区别
  • Comparable是在集合内部定义的方法实现的排序、Comparator实在集合外部实现的排序
  • Comparable位于java.lang包下边,Comparator位于java.util包下边
  • Comparable接口是自己完成比较,内部实现,Comparator是一个专用的比较器,是外部程序实现比较,外部实现
  如果自定义的类需要在加入List容器中能够排序,可以实现Comparable接口;而如果用Collection类的sort方法排序时,如果不指定Comparator接口,那么就以自然顺序排序。还有一点就是这里需要理解两个方法:Collections.sort和Arrays.sort,先看看两个方法的定义:
Collections.sort:
public static <extends Comparable<? extends T>> void sort(List<T> list) throws ClassCastException,UnsupportedOperationException
public static <T> void sort(List<T> list,Comparator<? super T> c) throws ClassCastException,UnsupportedOperationException
Arrays.sort:
public static void sort(byte[] a)
public static void sort(byte[] a,int fromIndex,int toIndex) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
public static void sort(char[] a)
public static void sort(char[] a,int fromIndex,int toIndex) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
public static void sort(double[] a)
public static void sort(double[] a,int fromIndex,int toIndex) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
public static void sort(float[] a)
public static void sort(float[] a,int fromIndex,int toIndex) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
public static void sort(int[] a)
public static void sort(int[] a,int fromIndex,int toIndex) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
public static void sort(long[] a)
public static void sort(long[] a,int fromIndex,int toIndex) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
public static void sort(Object[] a)
public static void sort(Object[] a,int fromIndex,int toIndex) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
public static void sort(short[] a)
public static void sort(short[] a,int fromIndex,int toIndex) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
public static void sort(T[] a,Comparator<? super T> c) throws ClassCastException
public static void sort(T[] a,int fromIndex,int toIndex,Comparator<super T> c) throws ClassCastException,IllegalArgumentException,ArrayIndexOutOfBoundsException
  所以从这里可以看到,Collection.sort方法和Arrays.sort方法传入的参数本身有区别,从这里就可以理解上边两段代码的使用方法了。

2.常用集合——列表、队列、栈
  上边一个章节已经介绍了JCF(Java Collection Framework)里面的四个主要顶层接口以及里面常用的一些,接下来看看在使用的时候的一些详细内容,还是先看看JCF里面的类的整体的一个继承结构,先看java.util包里面的所有常用集合类
  [I]表示接口[C]表示具体类[E]表示异常类[A]表示抽象类[S]表示静态类[F]表示不可子类化的类【非java.util包里面的这里会用全名】
  java.lang.Object
  |—[A]AbstractCollection<E>(1.2)
    |—[A]AbstractList<E>(1.2)
      |—[A]AbstractSequentialList<E>(1.2)
        |—[C]LinkedList<E>(1.2)
      |—[C]ArrayList<E>(1.2)
        |—[C]AttributeList(1.5)
        |—[C]javax.management.relation.RoleList(1.5)
        |—[C]javax.management.relation.RoleUnresolvedList(1.5)
      |—[C]Vector<E>(1.0)
        |—[C]Stack<E>(1.0)
    |—[A]AbstractQueue<E>(1.2)
      |—[C]java.util.concurrent.ArrayBlockingQueue<E>(1.5)
      |—[C]java.util.concurrent.ConcurrentLinkedQueue<E>(1.5)
      |—[C]java.util.concurrent.DelayQueue<E>(1.5)
      |—[C]java.util.concurrent.LinkedBlockingDeque<E>(1.6)
      |—[C]java.util.concurrent.LinkedBlockingQueue<E>(1.5)
      |—[C]java.util.concurrent.PriorityBlockingQueue<E>(1.5)
      |—[C]PriorityQueue<E>(1.5)
      |—[C]java.util.concurrent.SynchronousQueue<E>(1.5)
    |—[A]AbstractSet<E>(1.2)
      |—[C]java.util.concurrent.ConcurrentSkipListSet<E>(1.6)
      |—[C]java.util.concurrent.CopyOnWriteArraySet<E>(1.5)
      |—[A]EnumSet<E>(1.5)
      |—[C]HashSet<E>(1.2)
        |—[F]javax.print.attribute.standard.JobStateReasons
        |—[C]LinkedHashSet<E>(1.4)
      |—[C]TreeSet<E>(1.2)
    |—[C]ArrayDeque<E>(1.2)
  |—[A]AbstractMap<K,V>(1.2)
    |—[C]java.util.concurrent.ConcurrentHashMap<K,V>(1.5)
    |—[C]java.util.concurrent.ConcurrentSkipListMap<K,V>(1.6)
    |—[C]EnumMap<K,V>(1.5)
    |—[C]HashMap<K,V>(1.2)
      |—[C]LinkedHashMap<K,V>(1.4)
      |—[F]javax.print.attribute.standard.PrinterStateReasons
    |—[C]IdentityHashMap<K,V>(1.4)
    |—[C]TreeMap<K,V>(1.2)
    |—[C]WeakHashMap<K,V>(1.2)
  |—[A]Dictionary<K,V>(1.0)
    |—[C]Hashtable<K,V>(1.0)
      |—[C]Properties
        |—[A]Provider
          |—[A]AuthProvider
      |—[C]UIDefaults
  【在该章节,不讨论java.util以外的包里面的相关类】
  先提供一个整体的代码段作为参考,进行所有常用集合的初始化行为:
  i.AbstractList<E>相关类(1.2)【主要先介绍常用的几个类】
  1)LinkedList<E>类:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>,Deque<E>,Cloneable,Serializable
  List接口的链表实现,实现了所有可选的列表操作,并且允许所有的元素(包括null)。除了实现List接口,LinkedList类还为在列表的开头以及结尾get、remove和insert元素提供了统一的命名方法,这些操作允许将链接列表用作堆栈、队列或双端队列
  【*:该实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了列表,则它必须保持外部同步。这一般通过自然封装该列表的对象进行同步操作完成,如果不存在该对象,则应该使用Collections.synchronizedList方法来“包装列表”。最好在创建时完成这一操作,以防止对列表进行以外的不同步访问。此类的Iterator和ListIterator方法返回的迭代器是快速失败的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的remove或add方法,其他任何时间任何方式的修改,都会抛出ConcurrentModificationException异常,因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。注:迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽可能抛出ConcurrentModificationException,因此,编写依赖于异常的程序的方式是错误的。】
  这里提供一个良好做法:迭代器的快速失败行为仅仅用来检测程序错误。
  先简单接触一下LinkedList<E>:
  ——[1]LinkedList到Array的转换:——
package org.susan.java.collection;

import java.util.LinkedList;
import java.util.List;
/**
 *一个简单的将链表转换为数组的类
 **/
public class LinkedListToArray {
    public static void main(String args[]){
        List<String> list = new LinkedList<String>();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        String[] letters = new String[list.size()];
        list.toArray(letters);
        forint i = 0; i < letters.length; i++ ){
            System.out.println("Letters = " + letters[i]);
        }
    }
}
  上边代码简单说明了如何将一个LinkedList直接转换称一个Array,运行过后可以得到下边的结果:
Letters = A
Letters = B
Letters = C
Letters = D
  这是一个很简单的例子,接下来对比一个比较复杂的例子,下边是一个自定义的复杂的双链表:
  ——[2]自定义双链表:——
  下边这段代码没有使用任何集合类,仅仅使用原生的Java相关知识构建了一个比较简单的双链表:
package org.susan.java.collection;
/**
 *自定义链表节点,这段代码可以略过,或者说初学者觉得很复杂可以不看自定义部分直接学习下边的调用部分
 **/
class Link{
    public long dData;
    public Link next;
    public Link previous;
    public Link(long dData){
        this.dData = dData;
    }
    public void displayLink(){
        System.out.print(dData + " ");
    }
}

public class DoublyLinkedList {
    private Link first;
    private Link last;
    public DoublyLinkedList(){
        this.first = null;
        this.last = null;
    }
    public boolean isEmpty(){
        return (this.first == null);
    }
    public void insertFirst(long dData){
        Link newLink = new Link(dData);
        if(isEmpty()){
            this.last = newLink;
        }else{
            this.first.previous = newLink;
        }
        newLink.next = this.first;
        this.first = newLink;
    }
    public void insertLast(long dData){
        Link newLink = new Link(dData);
        if(isEmpty())
            this.first = newLink;
        else{
            this.last.next = newLink;
            newLink.previous = this.last;
        }
        this.last = newLink;
    }
    public Link deleteFirst(){
        Link temp = this.first;
        if( first.next == null )
            this.last = null;
        else {
            this.first.next.previous = null;
        }
        this.first = first.next;
        return temp;
    }
    public Link deleteLast(){
        Link temp = this.last;
        ifthis.first.next == null )
            this.first = null;
        else{
            this.last.previous.next = null;
        }
        this.last = this.last.previous;
        return temp;
    }
    public boolean insertAfter(long key,long dData){
        Link current = this.first;
        while(current.dData != key){
            current = current.next;
            if( current == null )
                return false;
        }
        Link newLink = new Link(dData);
        if( current == this.last){
            newLink.next = null;
            this.last = newLink;
        }else{
            newLink.next = current.next;
            current.next.previous = newLink;
        }
        newLink.previous = current;
        current.next = newLink;
        return true;
    }
    public Link deleteKey(long Key){
        Link current = this.first;
        while(current.dData != Key){
            current = current.next;
            if(current == null)
                return null;
        }
        if( current == this.first)
            this.first = current.next;
        else 
            current.previous.next = current.next;
        if( current == last)
            this.last = current.previous;
        else {
            current.next.previous = current.previous;
        }
        return current;
    }
    public void displayForward(){
        System.out.print("List (first to last): ");
        Link current = this.first;
        while(current != null){
            current.displayLink();
            current = current.next;
        }
        System.out.println("");
    }
    public void displayBackward(){
        System.out.print("List: ");
        Link current = this.last;
        while(current != null){
            current.displayLink();
            current = current.previous;
        }
        System.out.println();
    }
    public static void main(String args[]){
        DoublyLinkedList theList = new DoublyLinkedList();
        theList.insertFirst(22);
        theList.insertFirst(44);
        theList.insertLast(33);
        theList.insertLast(55);
        theList.displayForward();
        theList.displayBackward();

        theList.deleteFirst();
        theList.deleteLast();
        theList.deleteKey(11);
        theList.displayForward();

        theList.insertAfter(22, 77);
        theList.insertAfter(33, 88);
        theList.displayForward();
    }
}
  根据上边这种自定义双链表结构的操作,可以得到如下的输出
List (first to last): 44 22 33 55 
List: 55 33 22 44 
List (first to last): 22 33 
List (first to last): 22 77 33 88 
  以下版本是直接使用Java Collection自带的版本,这里做一个简单的比较:
  ——[3]直接使用双链表:——
package org.susan.java.collection;

import java.util.Iterator;
import java.util.LinkedList;
/**
 *使用原生的LinkedList类完成上边同样的操作和结果
 **/
public class DoublyLinkedListTwo {
    public static void main(String args[]){
        LinkedList<Integer> theList = new LinkedList<Integer>();
        theList.addFirst(22);
        theList.offerFirst(44);
        theList.addLast(33);
        theList.offerLast(55);
        System.out.println(theList);

        // LinkedList的逆序操作
        LinkedList<Integer> resultList = new LinkedList<Integer>();
        Iterator<Integer> iterator= theList.descendingIterator();
        while(iterator.hasNext()){
            resultList.add(iterator.next());
        }
        System.out.println(resultList);

        theList.removeFirst();
        theList.removeLast();
        //theList.remove(11); 该注释行需要注意
        System.out.println(theList);

        theList.add(1,77);
        theList.add(3,88);
        System.out.println(theList);
    }
}
  运行上边这段代码可以得到以下输出:
[44, 22, 33, 55]
[55, 33, 22, 44]
[22, 33]
[22, 77, 33, 88]
  从整体结构上看,两段代码产生的结果是一样的,为了进行概念说明,第二段代码故意使用了两种不同的提供方式进行添加操作,需要注意以下几点:
  • 针对LinkedList而言,添加都有三种不同的方式add/offer,addFirst/offerFirst,addLast/offerLast,三种方法都可以添加元素到LinkedList,而且添加位置各不相同
  • LinkedList的add方法有一个重载方法add(int index,E element),是讲元素插入到某个索引位置,上边代码和我们自定义代码不一样的,自定义代码是直接插入到某个元素后边,而LinkedList只能使用索引位置来判断,但是可以通过LinkedList提供的indexOf(Object)方法将下边两行代码替换:
    theList.add(1,77);
    theList.add(3,88);

    替换过后的代码为:
    theList.add(theList.indexOf(22)+1,77);
    theList.add(theList.indexOf(33)+1,88);

    这里需要说明的是,我们使用这段概念说明代码的目的是为了比较使用集合的某些操作,无实际意义,根据这段代码可以知道,LinkedList在插入元素的时候是使用的索引前插,如果在修改代码里面不加1的话,最后一行输出为会成为:[77, 22, 88, 33]
  • LinkedList的descendingIterator会生成逆序迭代器,直接将LinkedLink里面的元素进行逆向遍历
  • LinkedList提供了一个方法set(int index,element)可以直接替换某个索引位置的该元素
  • 最后一点是需要注意的就是注释的那一行theList.remove(11),如果不注释,这段程序将收到下边的异常处理:
    Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 11, Size: 2
    这一点和前边自定义的就不同了,实际上是这样的,LinkedList定义了两个带参数的remove方法:remove(int index)和remove(Object o),当代码直接使用theList.remove(11)的时候,匹配出来的方法是remove(int index),所以这种情况下如果索引不存在就会是上边的异常,而且传入的应该是索引;如果要使得上边的代码称为我们自定义LinkedList里面所表示的意义,就需要写成:
    remove(new Integer(11)); // 注:该方法不会抛出异常信息,不存在该元素的时候返回特殊值false
  【*:需要注意的是,如果使用List<Integer> theList = new LinkedList<Integer>()的时候,需要了解一点就是List接口里面并不包含addFirst等系列方法,有可能需要“向下转型”才能使用LinkedList类里面的方法,上边的代码主要是提供了LinkedList的特殊方法相关内容,面向接口编程设计的时候不是一个良好设计,这里只是为了说明LinkedList类的特殊方法相关特性。如果不使用indexOf方法,可以直接使用Deque<Integer> theList = new LinkedList<Integer>(),因为程序里面用到的很多“破坏性”方法都是Deque接口里面存在的,因为LinkedList从定义上可以知道实现了两个集合接口List<E>,Deque<E>】

  2)ArrayList<E>类
public class ArrayList<E> extends AbstractList<E> implements List<E>,RandomAccess,Cloneable,Serializable
  List接口的大小可变数组的实现,实现了所有可选列表操作,并允许包括null在内的所有元素。除了实现List接口之外,此类还提供了一些方法来操作内部用来存储列表的数组的大小。该类和Vector类等同,唯一的区别在于Vector是线程同步的,而该类是非同步的。该类里面的size、isEmpty、get、set、iterator和listIterator次操作都是以固定时间运行的。add操作以分片的固定时间运行,意思就是n个元素需要O(n)时间。其他所有操作都以线性时间运行(大体上讲),与用于LinkedList的实现对比,该实现的常量银子相对较低。
  每个ArrayList实例都有一个容量,该容量是指用来存储列表元素数组的大小。它总是小于等于列表的大小,随着向ArrayList中不断添加元素,该容量会自动增长。未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。在添加大量的元素之前,可以使用ensureCapacity来增加ArrayList的容量,这样可以减少递增式再分配的数量。
  【*:该实现不是同步的,这一点和LinkedList的注意事项是一模一样的。】
  接下来再看一些实例代码(因为ArrayList是一个十分常用的类,所以提供的代码段可能相对较多):
  ——[1]ArrayList的遍历——
package org.susan.java.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
 *三种不同的方法遍历ArrayList
 **/
public class ArrayListIterator {
    public static void main(String args[]){
        List<String> list = new ArrayList<String>();
        list.add("Monday");
        list.add("Tuesday");
        list.add("Wednesday");
        list.add("Thursday");
        list.add("Friday");
        list.add("Saturday");
        list.add("Sunday");
        Iterator<String> iterator = null;
        iterator = list.iterator();
        //使用while语句进行遍历
        while(iterator.hasNext()){
            String element = iterator.next();
            System.out.print(element + ",");
        }
        System.out.println("");
        //使用for语句进行遍历
        for( iterator = list.iterator(); iterator.hasNext();){
            String element = iterator.next();
            System.out.print(element + ",");
        }
        System.out.println("");
        //使用for增强语法进行遍历
        forString element: list){ 
            //JDK 1.5才出现的加强循环,以前的版本里面不允许这样的遍历方式
            System.out.print(element + ",");
        }
    }
}
  可以自己尝试写一段评测程序来评测,其实会发现三种遍历的效率差不多,所以可以根据需求自己选择适合的遍历方式,上边这段代码的输出这里就不写了。不仅仅如此,可以直接使用ArrayList的方法get(index)从某个索引位置取出该元素,只是折中取元素的方式是线性遍历整个列表,所以除了上边三种遍历方式,还有一种遍历方式,也是我们平时用得比较多的循环遍历。
forint i = 0; i < list.size(); i++ )
{
    String element = list.get(i);
    //String element = (String)list.get(i); 被注释的这部分是老版本的写法,就1.4之前的版本
}
  【*:泛型后边会讲到,而且泛型本身是从1.5版本的JDK才开始支持的,以前的版本使用的方式都是进行转型,所有集合返回的时候返回的都是Object类型的,所以老版本一般是进行转型操作。】
  ——[2]ArrayList的元素修改和读取——
package org.susan.java.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
 *读取和修改ArrayList里面的元素,老版本和新版本
 **/
public class ArrayListModify {
    public static void main(String args[]){
        List<String> genList = new ArrayList<String>();
        List oldList = new ArrayList();
        genList.clear();
        oldList.clear();
        genList.add("One");
        genList.add("Two");
        genList.add("Three");
        oldList.add("Old One");
        oldList.add("Old Two");
        oldList.add("Old Three");
        //输出构建完毕过后的ArrayList
        displayTwoList(genList, oldList);
        // 修改genList的索引为0和2位置的元素
        genList.set(0, "Changed At Index 0");
        genList.set(2, "Changed At Index 2");
        // 将oldList从genList索引位置为2的地方添加到genList【向前插入】
        genList.addAll(2, oldList);
        displayTwoList(genList, oldList);
    }
    private static void displayTwoList(List<String> list,List list2){
        System.out.println("----------------------------------------");
        System.out.print("Generics Style:");
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext()){
            //带泛型的新版本的读取,这里不需要转型
            String element = iterator.next();
            System.out.print("[" + element + "],");
        }
        System.out.println();
        System.out.print("Old Style:");
        Iterator iterator2 = list2.iterator();
        while(iterator2.hasNext()){
            //JDK1.4以及相关老版本的读取,这里需要执行强制转换
            String element = (String)iterator2.next();
            System.out.print("[" + element + "],");
        }
        System.out.println();
        System.out.println("----------------------------------------");
    }
}
  给定代码段的输出为:
----------------------------------------
Generics Style:[One],[Two],[Three],
Old Style:[Old One],[Old Two],[Old Three],
----------------------------------------
----------------------------------------
Generics Style:[Changed At Index 0],[Two],[Old One],[Old Two],[Old Three],[Changed At Index 2],
Old Style:[Old One],[Old Two],[Old Three],
----------------------------------------
  这段代码有几个地方需要说明:
  • 新版本的元素读取和旧版本的元素读取不一样,iterator调用next()的时候返回的是一个String,因为iterator的定义为Iterator<Integer>,而iterator2调用next()的时候返回的是Object,所以需要(String)iterator2.next();
  • 在调用Collection.addAll(int index,Collection<? extends E> c)方法的时候,使用的和前边一样是索引前插,就是将需要插入的集合的元素按照原集合的顺序插入到该索引元素之前
  ——[3]ArrayList去除重复元素——
package org.susan.java.collection;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
 *删除ArrayList中的重复元素
 **/
public class DistinctArrayList {
    public static void main(String args[]){
        List<String> arrayList = new ArrayList<String>();
        arrayList.add("A");
        arrayList.add("A");
        arrayList.add("B");
        arrayList.add("B");
        arrayList.add("C");
        arrayList.add("C");
        HashSet<String> hashSet = new HashSet<String>(arrayList);
        List<String> arrayList2 = new ArrayList<String>(hashSet);
        for(String item: arrayList2){
            System.out.print(item + ",");
        }
    }
}
  上边代码段的输出为:
A,B,C,
  简单分析一下:因为HashSet集合本身是不允许添加重复元素的,一旦是Set集合就拥有Set集合的特性就是无重复,所以在构造的时候hashSet实例实际只有三个元素了,所以这种方法去除重复元素也不失为一种很好的办法。
  ——[4]Collections类的相关用法——
package org.susan.java.collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsMethods {
    public static void main(String args[]){
        List<Integer> arrayList = new ArrayList<Integer>();
        arrayList.add(1);
        arrayList.add(4);
        arrayList.add(3);
        arrayList.add(2);
        arrayList.add(5);
        System.out.println("Before Sort:" + arrayList);
        Collections.sort(arrayList);
        System.out.println("After Sort:" + arrayList);
        System.out.println("Max Value:"+ Collections.max(arrayList));
        System.out.println("Min Value:" + Collections.min(arrayList));
        System.out.println("Before Swap:" + arrayList);
        Collections.swap(arrayList, 0, 3);
        System.out.println("Before Second Swap:" + arrayList);
        Collections.swap(arrayList, 0, 1);
        System.out.println("Before Reverse:" + arrayList);
        Collections.reverse(arrayList);
        System.out.println("After Reverse:" + arrayList);
        Collections.shuffle(arrayList);
        System.out.println("After shuffle:" + arrayList);
    }
}
  下边是上边这段程序的输出:
Before Sort:[1, 4, 3, 2, 5]
After Sort:[1, 2, 3, 4, 5]
Max Value:5
Min Value:1
Before Swap:[1, 2, 3, 4, 5]
Before Second Swap:[4, 2, 3, 1, 5]
Before Reverse:[2, 4, 3, 1, 5]
After Reverse:[5, 1, 3, 4, 2]
After shuffle:[4, 3, 2, 5, 1]
  根据上边的代码段,简单说明一下Collections类:该类在集合上进行操作或者返回集合的静态方法组构成,它包含了在集合上操作的多态算法,即“包装器”,包装器返回指定的集合支持的新机和,以及少数其他内容。
  • Collections.sort(List<T> list):对一个List进行排序,传入的对象必须是实现了List接口的实例
  • Collections.swap(List<?> list,int i, int j):将传入的列表索引为i和索引为j位置的元素进行置换
  • Collections.reverse(List<?> list):将传入的列表进行逆序排列过后输出
  • Collections.shuffle(List<?> list):将传入的列表打乱,进行乱序处理
  • Collections.max(Collection<? extends T> coll):获取该列表里面的最大值,给定比较器Comparator
  • Collections.min(Collection<? extends T> coll):获取该列表里面的最小值,给定比较器Comparator

  3)Vector<E>类
public class Vector<E> extends AbstractList<E> implements List<E>,RandomAccess,Cloneable,Serializable
  Vector类可以实现可增长的对象数组,和数组一样,它包含了可以使用整数索引进行访问的组件。但是,Vector的大小可以根据需要增长或缩小,以适应创建Vector后进行添加或者移除项的操作。每个向量会试图通过维护capacity和capacityIncrement来优化存储管理。capacity始终至少应与向量的大小相等,这个值通常比后者大些,因为随着将组件添加到向量中,其存储将按capacityIncrement的大小增加存储块。应用程序可以在插入大量组件前增加向量的容量;这样就减少了增加的重分配的量。
  【*:Vector的iterator和listIterator方法返回的迭代器是快速失败的,如果在迭代器创建后的任意时间从结构上修改了向量(通过迭代器自身的remove和add方法之外的任何方式),迭代器将抛出ConcurrentModificationException的异常,因此面对并发的修改,迭代器很快就完全失败了,而不是冒着在将来不确定的时间任意发生不确定行为的风险。有一点和其他几个不一样的:Vector的Element方法返回的Enumeration不是快速失败的。】
  ——[$]Vector基本用法——
package org.susan.java.collection;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/**
 *演示Vector里面的大部分基本用法的代码
 **/
public class VectorTester {
    public static void main(String args[]){
        // 按照不同的方式创建一个Vector
        List<Integer> list = new ArrayList<Integer>();
        list.add(45);
        list.add(34);
        Vector<Integer> vector = new Vector<Integer>();
        vector.add(12);
        vector.add(0, 44);
        vector.addAll(list);
        vector.addAll(2,list);
        vector.addElement(66);
        print(vector);
        System.out.println();
        System.out.println("First Element:" + vector.firstElement());
        // 删除索引为3的地方的元素
        vector.insertElementAt(35, 3);
        vector.removeElementAt(0);
        vector.removeElement(12);
        print(vector);
    }
    private static void print(Vector<Integer> vector){
        // Vector的遍历
        System.out.println("Enumeration:");
        Enumeration<Integer> enumeration = vector.elements();
        while(enumeration.hasMoreElements()){
            System.out.print("[" + enumeration.nextElement() + "],");
        }
        System.out.println();
        System.out.println("Iterator:");
        Iterator<Integer> iterator = vector.iterator();
        while(iterator.hasNext()){
            System.out.print("[" + iterator.next() + "],");
        }
    }
}
  先看看上边代码段的输出:
Enumeration:
[44],[12],[45],[34],[45],[34],[66],
Iterator:
[44],[12],[45],[34],[45],[34],[66],
First Element:44
Enumeration:
[45],[35],[34],[45],[34],[66],
Iterator:
[45],[35],[34],[45],[34],[66],
  针对Vector集合内容不多,主要说明两点:
  [1]Vector和ArrayList的区别:
  • 同步性:Vector是同步的,这个类中的一些方法保证了Vector中的对象是线程安全的,而ArrayList是异步操作,也就是说针对ArrayList中的操作并不是线程安全的。因为同步的要求会影响执行的效率,所以如果不需要线程安全的集合,那么ArrayList是一个很好的选择,同样可以避免一部分开销。从整体开销上讲,同步的集合的开销本身比异步的开销要大
  • 数据增长:从内部实现原理上看ArrayList和Vector都是使用了Array来控制集合中的对象,当进行添加操作的时候,如果元素的数目超过了内部数组目前的长度都需要扩展内部数组的长度,Vector缺省情况下自动增长是直接增长为原来容量的一倍,而ArrayList会自动增长原来容量的50%,所以最后不论用什么集合得到的空间总是比实际需要大很多。如果要在集合中保存大量的数据那么使用Vector是存在一定的优势的,因为可以通过设置集合的大小来避免不必要的资源开销。
  • 使用模式:在ArrayList和Vector中,从一个索引位置查找数据或是在集合的末尾添加、移除一个元素所花费的时间是一样的,这个时间表示为O(1),但是,如果在集合的其他位置添加或者移除一个元素花费的时间会成为线性开销:O(n-i),其中n代表整个集合中元素的个数,i代表添加或者移除元素索引的位置。
  [2]说明以下Vector的特殊API【这里主要是指Vector关于Element的相关操作】:
  • void addElement(obj):该方法类似add方法,是Vector独有的方法,添加一个元素到Vector集合
  • elementAt(int index):该方法等同于ArrayList的get方法【*:Vector也有get方法】,直接从索引为index的位置获取对应的集合元素
  • Enumeration<E> elements():该方法返回向量的一个枚举,上边代码已经使用过
  • firstElement():返回Vector向量中的第一个元素
  • void insertElementAt(obj,int index):在索引为index的位置插入某个元素【*:注意参数列表,因为很多集合第一个参数是索引,但是这个方法比较特殊,第二个参数是索引,第一个参数是元素本身。】
  • lastElement():返回该向量的最后一个元素
  • void removeAllElements():从该向量中移除所有的元素集,并且将向量的大小设置为0
  • boolean removeElement(Object obj):从该向量中移除某个元素,这个方法传入的非索引,而是元素本身,如果元素不存在就返回特殊值false
  • void removeElementAt(int index):从该向量中移除索引位置为index的元素【这个区别于普通remove方法的在于该方法不存在重载问题!】
  • protected void removeRange(int fromIndex,int toIndex):从该集合中移除索引位于fromIndex(包括)与toIndex(不包括)之间的所有元素
  • void setElementAt(obj,int index):设置索引为index位置的元素为obj,注意和List的set方法的参数列表,主要还是索引位置
  
  4)Stack<E>类
public class Stack<E> extends Vector<E>
  Stack类表示先进后出(LIFO)的对象堆栈,它通过五个操作对类Vector进行了扩展,允许将向量视为堆栈,并且提供了通常的push和pop操作,以及取堆栈定点的peek方法,检测堆栈是否为空的isEmpty方法,以及查找项并确定到堆栈顶距离的search方法
  【*:Deque接口以及实现提供了LIFO堆栈操作的更完整和更一致的集合,应该优先使用该集合,并非Stack。例如:Deque<Integer> stack = new ArrayDeque<Integer>();
  ——[$]基本用法——
package org.susan.java.collection;

import java.util.Stack;
/**
 *Stack类的基本用法
 **/
public class StackElement {
    public static void main(String args[]){
        Stack<String> stack = new Stack<String>();
        stack.push("[Java]");
        stack.push("[C++]");
        stack.push("[Python]");
        System.out.println("Next:" + stack.peek());
        stack.push("[Ruby]");
        stack.push("[JavaScript]");
        System.out.println("Pop:" + stack.pop());
        int count = stack.search("[Java]");
        System.out.println(count);
        System.out.println(stack);
    }
}
  运行这段代码会输出:
Next:[Python]
Pop:[JavaScript]
4
[[Java], [C++], [Python], [Ruby]]
  因为Stack是Vector的子类,所以Vector里面的很多方法Stack都可以使用,这里主要提及Stack的特殊方法列表,根据上边的代码:
boolean empty():判断堆栈是否为空,该方法正规的命名应该为isEmpty(),不要因为这个方法的名称误解,这个方法仅仅做判断,不做清空
peek():查看堆栈顶部的对象,但是不从顶部移除它
pop():移除堆栈顶部的对象,并且作为此函数的返回值
push(item):把item对象压入该堆栈
int search(Object o):针对Stack进行搜索,直接返回该元素的位置,不过需要区别于索引位置的是,这个位置的值基数是1,而不是索引常用的初始基数0

  ii.AbstractQueue<E>类
  【*:在介绍集合内容的时候没有介绍java.util.concurrent包里面的集合,例如DelayQueue这种,仅仅介绍常用的java.util包里面的集合】
  该类里面只有一个实现类需要介绍:
  PriorityQueue<E>类(1.5)
public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable
  该集合为一个基于优先级的无界优先队列,优先级队列的元素按照自然排序进行排序,或者根据内部的Comparator进行排序,主要是取决于构造方法。【*:该集合不允许使用null元素,而且在插入元素的时候不允许插入不可比较对象。】此队列的头是按照指定排序方式确定的最小元素,如果多个元素都是最小值,则头是其中一个元素【*:选择方式是随机的】。优先级队列是无界的,但是有一个内部的容量,控制用于存储元素的集合的大小,通常大于等于队列的大小,而且该队列不需要指定容量的控制策略,它会自己完成。
  该类的迭代器实现了Collection和Iterator的所有可选的方法,方法iterator()中的迭代器不保证以任何特定的顺序遍历里面的元素,如果需要顺序遍历,考略Arrays.sort(pq.toArray()),而且该实现是线程不安全的。
package org.susan.java.collection;

import java.util.Iterator;
import java.util.PriorityQueue;

public class PriorityQueueExample {
    public static void main(String args[]){
        PriorityQueue<Integer> queue = new PriorityQueue<Integer>();
        queue.add(23);
        queue.add(44);
        queue.add(66);
        Iterator<Integer> iterator = queue.iterator();
        while(iterator.hasNext()){
            System.out.print("[" + iterator.next() + "],");
        }
        System.out.println();
        queue.offer(34);
        iterator = queue.iterator();
        while(iterator.hasNext()){
            System.out.print("[" + iterator.next() + "],");
        }
    }
}
  以下是这段代码的输出:
[23],[44],[66],
[23],[34],[66],[44],
  这个类里面的方法和其他方法都大同小异,不过注意几个方法,该方法是从AbstractQueue继承过来的
  • element():该方法直接获取队列的头部元素,但是不移除
  • remove():该方法获取队列的头元素,但是会从该队列里面移除该元素