如果一个程序只包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序。
通常程序总是根据运行时才知道的某些条件去创建新对象。在此之前,不会知道所需对象的数量,甚至不知道确切的类型。因此需要在任意时刻和任意位置创建任意数量的对象。所以就不能依靠创建命名的引用来持有每一个对象:
MyType aReference;//例:int a=0;
因为你不知道实际上会需要多少这样的引用。
此时我们就需要一个容器来保存我们的对象,比如数组。在Java中有一套相当完整的容器来解决这个问题
- 基本类型为List、Set、Queue和Map,我们也称这些对象类型为容器类。
- Java的容器类都可以自动地调整自己的尺寸,我们不需要担心容器应该为多大。
基本概念
Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
- Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,Set不能有重复元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
- Map。一组成对的“键值对”对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值,因此从某种意义上讲,它将数字与对象关联在一起。映射表允许我们使用另一个对象来查找某个对象,它也被成为“关联数组”,因为它将某些对象与另外一些对象关联在一起;或被成为“字典”,因为你可以使用键对象来查找值对象,就像在字典中用单词来定义一样。Map是强大的编程工具。
List
List承诺可以将元素维护在特定的序列中。List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入和移除元素。
有两种类型的List:
- 基本的ArrayList,它长于随机访问元素,但是在List的中间插入和移除元素时较慢。
- LinkedList,它通过代价较低的在List中间进行插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但它的特性集较ArrayList更大。
List允许在它被创建之后添加元素、移除元素、或者自我调整尺寸。下面给出几个常用方法:
- contains()确认某个对象是否还在列表中
- add()在末尾添加一个元素
- removed()移除一个元素,将移除元素的引用传递给remove()
- indexOf()发现元素在List中所处位置的索引编号
- subList()从原有List获得一个数组片段,参数为两个索引,第一个为起始位置,第二个为终点位置
- containsAll()确认另一个列表是否存在与该列表中
- retainAll()交集操作,参数为另一个List,保留两个数组的共同元素。
- removedAll()移除共同元素,参数为另一个List
- set()两个参数,第一个为索引,第二个为元素,用参数元素代替索引位置的元素,即代替
- addAll()在列表末尾添加一串元素参数为另一个List,可以在该参数前加一个索引参数,使其从索引位置后面添加这串元素
- isEmpty()列表是否为空,不为空返回false
- clear()清空列表元素
- toArray()返回Object数组,可以传递目标类型的数据使其返回指定类型的数组;例:int[] a = b.toArray(new int[0]);\\b是个List
LinkedList
LinkedList还添加了使其用作栈、队列或双端队列的方法。其实LinkedList就是我们数据结构中的链表。具体用法百度一下有更详细的介绍。
迭代器
- 迭代器的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。
- 迭代器通常被成为轻量级对象:创建它的代价小。因此经常见到对迭代器有些奇怪的限制,例如Java的Iterator()只能单向移动。
Iterator:
- 使用Iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
- 使用next()获得序列的下一个元素
- 使用hasNext()检查序列中是否还存在元素
- 使用remove()将迭代器新近返回的元素删除
Class A{
public static void main(String[] arg){
List<B> bs = B.arrayList(10);//该函数返回一个随机填充的B类型的ArrayList
Iterator it = bs.iterator();
while(it.hasNext()){
B b = it.next();
print("b");
it.remove();
}
}
}
不论是List还是Set等其他类型的容器,都能通过iterator()转化为Iterator类型,而且转化后Iterator便不必考虑容器类型。
ListIterator:
- ListIterator是更强大的Iterator的子类型,它只能用于各种List类的访问
- ListIterator可以双向移动,previous()向前,next()向后,有hasNext()就有hasPrevious()
- 可以使用set()方法替换它访问过的最后一个元素
- 可以调用listIterator(n)创建一个一开始就指向列表索引为n的元素处的ListIterator
Set
Set不保存重复的元素。Set中最常被使用的是测试归属性(基于对象的值确定归属性),实际上Set具有和Collection完全一样的接口,可以说Set就是Collection,只是行为不同。
- 我们最常用的Set是HashSet,HashSet出于速度原因考虑使用了散列,因此排列是不规则的(后面有深入容器的博文会讲到散列),此外TreeSet将元素存储在红-黑数据结构中,LinkedHashList因为查询速度的原因也使用了散列,但它看起来使用了链表维护元素的插入顺序
- 最常用的操作之一:使用contains()测试Set的归属性
Map
一组成对的“键值对”对象,允许你使用键来查找值。
- get()参数为键对象,如果不存在则返回null
- 可以用containsKey()和containsValue()来测试一个Map,以便查看它是否包含某个键或某个值
- 可以通过将值设置为Map或其他容器类将Map扩展到多维,如Map<String,List<String>>
Queue
队列是一个典型的先进先出(FIFO)的容器。队列长被当作一可靠的将对象从程序的某个区域传输到另一个区域的途径。
LinkedList提供了方法以支持队列的行为,并实现了Queue接口,可以将LinkedList向上转型为Queue。如:
Queue<Integer> queue = new LinkedList<Integer>();
这样转型可以窄化对LinkedList的方法访问权,使得只有强档的方法才可以使用。
常用函数:
- offer()在允许的情况下,将一个元素插到队尾,或者返回false
- peek()和element()都将在不移除的情况下返回队头,但peek()在队列为空时返回null,而element()会抛出NoSuchElementException异常
- poll()和remove()都将移除并返回队头,但poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常
PriortyQueue
先进先出声明的是下一个元素应该是等待时间最长的元素。优先级队列声明下一个弹出的元素是最需要的元素(具有最高的优先级)。PriorityQueue就是优先级队列。
当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序,默认的排序将使用对象在队列中的自然顺序,但你可以通过提供自己的Comparator来修改这个顺序。PriorityQueue可以确保当你调用peek()、poll()he remove()方法时,获取的元素将是队列中优先级最高的元素。
自然顺序:例Integer型值越小优先级越高
Integer、String、Character已经内建了自然排序,如果想要使用自己的类就必须提供自己的Comparator(方法将在后面博文讲到)。
Foreach
foreach能与所有的Collection对象一起工作
Class Test{
public static void main(String[] arg){
Collection<String> cs = new LinkedList<String>();
Collections.addAll(cs,"Take the long way home".split(" "));
for(String s:cs){
print(s+", ");
}
}
}
能够工作的原因是Java SE5引入了新的被成为Iterable的接口,该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此如果你创建了任何实现Iterable的类,都可以将它用于foreach语句中。
以上总结自《Java编程思想(Thinking in Java)》——YayayaHong