观察Java容器的简图。有Map、List、Set和Queue四种容器。常用的容器用黑色粗线框标出,点线框表示接口,实线框表示具体的类,空心箭头表示实现接口,实心箭头表示某个类可以生成所指向的对象。
Java容器类的用途是“保存对象”,分为Collection和Map两个概念。
Collection:独立元素的序列,包括List、Set、Queue。
Map:一组成对的“键值对”对象。包括HashMap和TreeMap。
List:以特定顺序保存的一组元素。
两种类型:
ArrayList:长于随机访问元素
LinkedList:长于在List中间进行插入和删除操作,提供了优化的顺序访问,特性集更大。
Stack:栈,“先进后出”,是通过LinkedList实现的。
(Java1.0的Stack设计欠佳,现在默认的Stack都是使用net.mindview.util而不是原来的java.util.Stack。使用后者需要使用全限定名称)
Set:元素不能重复。
HashSet:最快的查询速度。使用散列函数存储。
TreeSet:保持元素处于排序状态。使用红黑树存储。
LinkedHashSet:以插入顺序保存元素。使用链表维护元素的插入顺序,使用散列加快查询。
Queue:队列,“先进先出”。
PriorityQueue:优先队列,弹出优先级最高的元素。
HashMap:用于快速访问。
TreeMap:保持“键”始终处于排序状态。
LinkedHashMap:保持元素插入的顺序,使用散列加快查询。
关于上述具体类的使用不做更多解释,需要使用时自会使用。
创建容器实例:
应用预定义的泛型:
使用尖括号括起来的是参数类型(可以有多个),它指定了这个容器实例可以保存的类型。
上转型:
一般创建一个具体类的对象,将其转换为对应的接口,然后在其余的代码中都使用这个接口。
List
使用接口的目的:当需要改变具体类的实现的时候,只需要在创建的地方修改它。当然,也会有例外的情况,比如当某些类具有额外的实现的时候,像LinkedList具有在List接口中未包含的额外方法。
迭代器:
迭代器(也是一种设计模式)是一个对象,它的工作是遍历并选择序列中的对象。
迭代器通常被称为轻量级对象:创建的代价小。
Java的Iterator只能单向移动。
Iterator用法:
1. 使用方法iterator()为容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
2. 使用next()获得序列中的下一个元素。
3. 使用hasNext()检查序列中是否还有元素。
4. 使用remove()将迭代器新近返回的元素删除。
使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}
ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问。ListIterator可以双向移动。
ListIterator的用法:
1. listIterator()返回的ListIterator指向开始位置,listIterator(n)返回的ListIterator指向列表索引为n的元素处。
2. add(E e): 将指定的元素插入列表,插入位置为迭代器当前位置之前
3. hasNext():正向遍历,如果列表迭代器后面还有元素,则返回 true,否则返回false
4. hasPrevious():逆向遍历,列表迭代器前面还有元素,则返回 true,否则返回false
5. next():返回列表中ListIterator指向位置后面的元素
6. nextIndex():返回列表中ListIterator所需位置后面元素的索引
7. previous():返回列表中ListIterator指向位置前面的元素
8. previousIndex():返回列表中ListIterator所需位置前面元素的索引
9. remove():从列表中删除next()或previous()返回的最后一个元素
10. set(E e):用e替换它访问的最后一个元素。
容器类的共性:
Collection是描述所有序列容器共性的根接口。
Java遵循C++的方式,使用迭代器而不是Collection来表示容器之间的共性。
实现Collection意味着需要提供Iterator()方法。
Java SE5引入了Iterator接口,该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。
Foreach:
Collection是对Iterable接口的拓展。故所有的Collection对象都可以使用foreach方式,对元素进行方便的遍历。
import java.util.*;
public class ForEachCollections {
public static void main(String[] args) {
Collection cs = new LinkedList();
Collections.addAll(cs,
"Take the long way home".split(" "));
for(String s : cs)
System.out.print("‘" + s + "‘ ");
}
}
输出:
‘Take’ ‘the’ ‘long’ ‘way’ ‘home’
容器的打印:
默认的打印行为(使用容器提供的toString()方法):
Collection打印结果用方括号括住,Map用花括号括住,键值对用等号联系。
看下面的例子:
public class PrintingContainers {
static Collection fill(Collection collection) {
collection.add("rat");
collection.add("cat");
collection.add("dog");
collection.add("dog");
return collection;
}
static Map fill(Map map) {
map.put("rat", "Fuzzy");
map.put("cat", "Rags");
map.put("dog", "Bosco");
map.put("dog", "Spot");
return map;
}
public static void main(String[] args) {
print(fill(new ArrayList()));
print(fill(new LinkedList()));
print(fill(new HashSet()));
print(fill(new TreeSet()));
print(fill(new LinkedHashMap()));
print(fill(new HashMap()));
print(fill(new TreeMap()));
print(fill(new LinkedHashMap()));
}
}
输出:
[rat, cat, dog, dog]
[rat, cat, dog, dog]
[cat, dog, rat]
[cat, dog, rat]
{cat=Rags, dog=Spot, rat=Fuzzy}
{cat=Rags, dog=Spot, rat=Fuzzy}
{rat=Fuzzy, cat=Rags, dog=Spot}
适配器方法惯用法:
我们希望在默认前向迭代器的基础上,添加产生反向迭代器的能力。
为了保存原来的前向迭代器功能,我们不选择覆盖,而是添加一个能够产生Iterable对象的方法,该对象可以用于foreach语句。
package cn.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
class ReversibleArrayList extends ArrayList {
public ReversibleArrayList(Collection c) { super(c); }
public Iterable reversed() {
return new Iterable(){
@Override
public Iterator iterator() {
return new Iterator(){
int current = size() - 1;
public boolean hasNext() {
return current > -1;
}
public T next() { return get(current--); }
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
}
public class AdapterMethodIdiom {
public static void main(String[] args) {
ReversibleArrayList ral =
new ReversibleArrayList(
Arrays.asList("To be or not to be".split(" ")));
for (String s : ral) {
System.out.println(s + " ");
};
for (String s : ral.reversed()) {
System.out.println(s + " ");
};
}
}
输出:
To be or not to be
be to not or be To