第11章 持有对象

如果一个程序只包含固定数量的且其生命期都是已知的对象,那么这是一个非常简单的程序.

其中基本的类型如Set Queue 和Map,这些称为集合类.

1. 泛型和类型安全的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//: holding/ApplesAndOrangesWithoutGenerics.java
// Simple container example (produces compiler warnings).
// {ThrowsException}
import java.util.*;
class Apple {
private static long counter;
private final long id = counter++;
public long id() { return id; }
}
class Orange {}
public class ApplesAndOrangesWithoutGenerics {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ArrayList apples = new ArrayList();
for(int i = 0; i < 3; i++)
apples.add(new Apple());
// Not prevented from adding an Orange to apples:
apples.add(new Orange());
for(int i = 0; i < apples.size(); i++)
((Apple)apples.get(i)).id();
// Orange is detected only at run time
}
} /* (Execute to see output) *///:~

当在使用Apple的对象时,得到的只是Object引用,必须将其转型为Apple.因此,在调用Apple的id()方法之前,强制执行转型.

通过使用泛型,可以在编译器防止将错误类型的对象放置到容器中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//: holding/ApplesAndOrangesWithGenerics.java
import java.util.*;
public class ApplesAndOrangesWithGenerics {
public static void main(String[] args) {
ArrayList<Apple> apples = new ArrayList<Apple>();
for(int i = 0; i < 3; i++)
apples.add(new Apple());
// Compile-time error:
// apples.add(new Orange());
for(int i = 0; i < apples.size(); i++)
System.out.println(apples.get(i).id());
// Using foreach:
for(Apple c : apples)
System.out.println(c.id());
}
} /* Output:
0
1
2
0
1
2
*///:~

2. 基本概念

Java容器类类库的用途是”保存对象”.

  • Collection. 一个独立元素的序列,这些元素都服从一条或多条规则.List必须按照插入的顺序保存元素,而set不能有重复元素.Queue按照排队规则来确定对象产生的顺序.
  • Map.一组成对的”键值对”对象,允许你使用键来查找值.ArrayList允许你使用数字来查找值,从某种意义说,它将数字与对象关联在一起.映射表允许我们使用另一个对象来查找某个对象,它也被称为”关联数组”,因此它将某些对象与另外一些对象关联在一起;或者被称为”字典”,因此你可以使用键对象来查找值对象,就像在字典中使用单词来定义一样.
1
List<apple> apples = new ArrayList<Apple>();

ArrayList已经被向上转型为List.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//: holding/SimpleCollection.java
import java.util.*;
public class SimpleCollection {
public static void main(String[] args) {
Collection<Integer> c = new ArrayList<Integer>();
for(int i = 0; i < 10; i++)
c.add(i); // Autoboxing
for(Integer i : c)
System.out.print(i + ", ");
}
} /* Output:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
*///:~

只使用了Collection方法,因为任何继承自Collection的类的对象都可以正常工作.

add()方法的名称就表明它是要将一个新的元素防止到Collection中.注的是要确保Collection包含指定的元素.这是因为考虑到Set的含义,因为在Set中只有元素不存在的情况下才会添加.

3. 添加一组元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//: holding/AddingGroups.java
// Adding groups of elements to Collection objects.
import java.util.*;
public class AddingGroups {
public static void main(String[] args) {
Collection<Integer> collection =
new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));
Integer[] moreInts = { 6, 7, 8, 9, 10 };
collection.addAll(Arrays.asList(moreInts));
// Runs significantly faster, but you can't
// construct a Collection this way:
Collections.addAll(collection, 11, 12, 13, 14, 15);
Collections.addAll(collection, moreInts);
// Produces a list "backed by" an array:
List<Integer> list = Arrays.asList(16, 17, 18, 19, 20);
list.set(1, 99); // OK -- modify an element
// list.add(21); // Runtime error because the
// underlying array cannot be resized.
}
} ///:~

4. 容器的打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//: holding/PrintingContainers.java
// Containers print themselves automatically.
import java.util.*;
import static net.mindview.util.Print.*;
public class PrintingContainers {
static Collection fill(Collection<String> collection) {
collection.add("rat");
collection.add("cat");
collection.add("dog");
collection.add("dog");
return collection;
}
static Map fill(Map<String,String> 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<String>()));
print(fill(new LinkedList<String>()));
print(fill(new HashSet<String>()));
print(fill(new TreeSet<String>()));
print(fill(new LinkedHashSet<String>()));
print(fill(new HashMap<String,String>()));
print(fill(new TreeMap<String,String>()));
print(fill(new LinkedHashMap<String,String>()));
}
} /* Output:
[rat, cat, dog, dog]
[rat, cat, dog, dog]
[dog, cat, rat]
[cat, dog, rat]
[rat, cat, dog]
{dog=Spot, cat=Rags, rat=Fuzzy}
{cat=Rags, dog=Spot, rat=Fuzzy}
{rat=Fuzzy, cat=Rags, dog=Spot}
*///:~

目前只需要知道HashMap是最快的存储方式.

5. List

有两种类型的List :

  • 基本的ArrayList,它长于随机访问元素,但是在List的中间插入和移除元素时较慢.
  • LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问.LinkedList在随机访问方面比较慢,但是它的特性集较ArrayList更大.

6. 迭代器

Java的Iteratir只能单向移动,这个Iterator只能用来 :

  • 使用方法iterator()要求容器返回一个Iterator.Iterator将准备好返回序列的第一个元素.
  • 使用next()获得序列中下一个元素.
  • 使用hasNext()检查序列中是否还有元素.
  • 使用remove()将迭代器新近返回的元素删除.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//: holding/SimpleIteration.java
import typeinfo.pets.*;
import java.util.*;
public class SimpleIteration {
public static void main(String[] args) {
List<Pet> pets = Pets.arrayList(12);
Iterator<Pet> it = pets.iterator();
while(it.hasNext()) {
Pet p = it.next();
System.out.print(p.id() + ":" + p + " ");
}
System.out.println();
// A simpler approach, when possible:
for(Pet p : pets)
System.out.print(p.id() + ":" + p + " ");
System.out.println();
// An Iterator can also remove elements:
it = pets.iterator();
for(int i = 0; i < 6; i++) {
it.next();
it.remove();
}
System.out.println(pets);
}
} /* Output:
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster
[Pug, Manx, Cymric, Rat, EgyptianMau, Hamster]
*///:~

Iterator还可以移除由next()产生的最后一个元素,这意味着在调用remove()之前必须先调用next().

6.1 ListIterator

ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问.尽管Iterator只能想前移动,但是ListIterator可以双向移动.它还可以产生相对于迭代器在列表中指向的当前位子的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的最后一个元素.可以通过调用ListIterator()方法产生一个指向List开始处的ListIterator,并且还可以通过调用listIterator(n)方法创建一个一开始就指向列表索引为n的元素处的ListIterator.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//: holding/ListIteration.java
import typeinfo.pets.*;
import java.util.*;
public class ListIteration {
public static void main(String[] args) {
List<Pet> pets = Pets.arrayList(8);
ListIterator<Pet> it = pets.listIterator();
while(it.hasNext())
System.out.print(it.next() + ", " + it.nextIndex() +
", " + it.previousIndex() + "; ");
System.out.println();
// Backwards:
while(it.hasPrevious())
System.out.print(it.previous().id() + " ");
System.out.println();
System.out.println(pets);
it = pets.listIterator(3);
while(it.hasNext()) {
it.next();
it.set(Pets.randomPet());
}
System.out.println(pets);
}
} /* Output:
Rat, 1, 0; Manx, 2, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6, 5; Pug, 7, 6; Manx, 8, 7;
7 6 5 4 3 2 1 0
[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx]
[Rat, Manx, Cymric, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau]
*///:~

7. LinkedList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//: holding/LinkedListFeatures.java
import typeinfo.pets.*;
import java.util.*;
import static net.mindview.util.Print.*;
public class LinkedListFeatures {
public static void main(String[] args) {
LinkedList<Pet> pets =
new LinkedList<Pet>(Pets.arrayList(5));
print(pets);
// Identical:
print("pets.getFirst(): " + pets.getFirst());
print("pets.element(): " + pets.element());
// Only differs in empty-list behavior:
print("pets.peek(): " + pets.peek());
// Identical; remove and return the first element:
print("pets.remove(): " + pets.remove());
print("pets.removeFirst(): " + pets.removeFirst());
// Only differs in empty-list behavior:
print("pets.poll(): " + pets.poll());
print(pets);
pets.addFirst(new Rat());
print("After addFirst(): " + pets);
pets.offer(Pets.randomPet());
print("After offer(): " + pets);
pets.add(Pets.randomPet());
print("After add(): " + pets);
pets.addLast(new Hamster());
print("After addLast(): " + pets);
print("pets.removeLast(): " + pets.removeLast());
}
} /* Output:
[Rat, Manx, Cymric, Mutt, Pug]
pets.getFirst(): Rat
pets.element(): Rat
pets.peek(): Rat
pets.remove(): Rat
pets.removeFirst(): Manx
pets.poll(): Cymric
[Mutt, Pug]
After addFirst(): [Rat, Mutt, Pug]
After offer(): [Rat, Mutt, Pug, Cymric]
After add(): [Rat, Mutt, Pug, Cymric, Pug]
After addLast(): [Rat, Mutt, Pug, Cymric, Pug, Hamster]
pets.removeLast(): Hamster
*///:~

8. Stack

“栈”通常是制”后进先出(LIFO)”的容器,有时栈也被称为叠加栈,因为最后”压入”栈的元素,第一个”弹出”栈.经常用来类比栈的事物是装有弹簧的储放器中的自助餐托盘,最后装入的托盘总是最先拿出使用的.

LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用,不过,有时一个真正的”栈”更能把事情说清楚 :

1
2
3
4
5
6
7
8
9
10
11
12
13
//: net/mindview/util/Stack.java
// Making a stack from a LinkedList.
package net.mindview.util;
import java.util.LinkedList;
public class Stack<T> {
private LinkedList<T> storage = new LinkedList<T>();
public void push(T v) { storage.addFirst(v); }
public T peek() { return storage.getFirst(); }
public T pop() { return storage.removeFirst(); }
public boolean empty() { return storage.isEmpty(); }
public String toString() { return storage.toString(); }
} ///:~

通过使用泛型,引入了在栈的定义中最简单的可行示例.类名之后的告诉编译器这将是一个参数化类型,而其中的类型参数,即在类被使用时将会被实际类型替换的参数,就是T.

Set

Set不保存重复的元素,如果你试图将相同的对象的多个实例添加到Set中,那么它就会阻止这种重复现象.Set最常用被使用的是测试归数学,你可以很容易地询问某个对象是否在某个Set中.所以,查找就成为了Set中最重要的操作,因此可以选择一个Hashset的实现,它专门对快速查找进行了优化.

Set是基于对象的值来确定归属性的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//: holding/SetOfInteger.java
import java.util.*;
public class SetOfInteger {
public static void main(String[] args) {
Random rand = new Random(47);
Set<Integer> intset = new HashSet<Integer>();
for(int i = 0; i < 10000; i++)
intset.add(rand.nextInt(30));
System.out.println(intset);
}
} /* Output:
[15, 8, 23, 16, 7, 22, 9, 21, 6, 1, 29, 14, 24, 4, 19, 26, 11, 18, 3, 12, 27, 17, 2, 13, 28, 20, 25, 10, 5, 0]
*///:~

输出的顺序没有任何规则可循,着是因为出于速度,HashSet使用了散列.HashSet所维护的顺序与TreeSet或LinkedHashSet都不同,因为它们的实现具有不同的元素存储方式.TreeSet将元素存储在红-黑树数据结构中,而HashSet使用的是散列函数.

如想对结果排序,可以使用TreeSet来代替HashSet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//: holding/SortedSetOfInteger.java
import java.util.*;
public class SortedSetOfInteger {
public static void main(String[] args) {
Random rand = new Random(47);
SortedSet<Integer> intset = new TreeSet<Integer>();
for(int i = 0; i < 10000; i++)
intset.add(rand.nextInt(30));
System.out.println(intset);
}
} /* Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
*///:~
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//: holding/SetOperations.java
import java.util.*;
import static net.mindview.util.Print.*;
public class SetOperations {
public static void main(String[] args) {
Set<String> set1 = new HashSet<String>();
Collections.addAll(set1,
"A B C D E F G H I J K L".split(" "));
set1.add("M");
print("H: " + set1.contains("H"));
print("N: " + set1.contains("N"));
Set<String> set2 = new HashSet<String>();
Collections.addAll(set2, "H I J K L".split(" "));
print("set2 in set1: " + set1.containsAll(set2));
set1.remove("H");
print("set1: " + set1);
print("set2 in set1: " + set1.containsAll(set2));
set1.removeAll(set2);
print("set2 removed from set1: " + set1);
Collections.addAll(set1, "X Y Z".split(" "));
print("'X Y Z' added to set1: " + set1);
}
} /* Output:
H: true
N: false
set2 in set1: true
set1: [D, K, C, B, L, G, I, M, A, F, J, E]
set2 in set1: false
set2 removed from set1: [D, C, B, G, M, A, F, E]
'X Y Z' added to set1: [Z, D, C, B, G, M, A, F, Y, X, E]
*///:~

10. Map

考虑一个程序,它将用来检查Java的Random类的随机性.Random可以将产生理想的数字分部,在本例中,键是由Random产生的数字,而值是该数字出现的次数.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//: holding/Statistics.java
// Simple demonstration of HashMap.
import java.util.*;
public class Statistics {
public static void main(String[] args) {
Random rand = new Random(47);
Map<Integer,Integer> m =
new HashMap<Integer,Integer>();
for(int i = 0; i < 10000; i++) {
// Produce a number between 0 and 20:
int r = rand.nextInt(20);
Integer freq = m.get(r);
m.put(r, freq == null ? 1 : freq + 1);
}
System.out.println(m);
}
} /* Output:
{15=497, 4=481, 19=464, 8=468, 11=531, 16=533, 18=478, 3=508, 7=471, 12=521, 17=509, 2=489, 13=506, 9=549, 6=519, 1=502, 14=477, 10=513, 5=503, 0=481}
*///:~

在main()中,自动包装机将随机生成的int转换为HashMap可以使用Integer引用.如果键不在容器中,get()方法则返回null(这表示该数字第一次被找到).否则,get()方法将产生与该键相关联的Integer的值,然后这个值被递增(自动包装机制再次简化了表达式,但是确定发生了对Integer的包装和拆包).

11. Queue

队列是一个典型的先进先出(FIFO)的容器.即从容器的一端放入事物,从另一端取出,并请求事物放入容器的顺序与取出的顺序是相同的.队列常被当做一种可靠的将对象从程序的某个区域传输到另一个区域的途径.队列在并发编程中特别重要.

LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现.通过将LinkedList向上转型为Queue.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//: holding/QueueDemo.java
// Upcasting to a Queue from a LinkedList.
import java.util.*;
public class QueueDemo {
public static void printQ(Queue queue) {
while(queue.peek() != null)
System.out.print(queue.remove() + " ");
System.out.println();
}
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<Integer>();
Random rand = new Random(47);
for(int i = 0; i < 10; i++)
queue.offer(rand.nextInt(i + 10));
printQ(queue);
Queue<Character> qc = new LinkedList<Character>();
for(char c : "Brontosaurus".toCharArray())
qc.offer(c);
printQ(qc);
}
} /* Output:
8 1 1 1 5 14 3 1 0 1
B r o n t o s a u r u s
*///:~

offer()方法是与Queue相关的方法之一,它在允许的的情况下,将一个元素插入到队尾,或者返回false.peek()和element()都将在不移除的情况下返回队头,但是peek()方法在队列为空时返回null,而element()会抛出NoSuchElementException异常.poll()和remove()方法将移除并返回队头,但是poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常.

自动包装机制会自动将nextInt()方法的int结果转换为queue所需的Integer对象.将char 出转换为qc所需的Character对象.Queue接口窄化了对LinkedList的方法的访问权限,以使得只有适当的方法才可以使用.

11.1 PrionrityQueue

先进先出描述了最典型的队列规则.队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则.先进先出声明的是下一个运算应该是等待时间最长的元素.

优先级队列声明下一个弹出元素是最需要的元素.如果构建了一个消息系统,某些信息比其他信息更重要,因而应该更快得到处理,那么它们何时得到处理就与它们何时到达无关.

当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序.默认的排序将使用对象在队列中的自然排序.但是你可以通过提供自己的Comparator来修改这个顺序.PriorityQueue可以确保当你调用peek(),poll()和remove()方法时,获取的元素将是队列中优先级别最高的元素.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//: holding/PriorityQueueDemo.java
import java.util.*;
public class PriorityQueueDemo {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue =
new PriorityQueue<Integer>();
Random rand = new Random(47);
for(int i = 0; i < 10; i++)
priorityQueue.offer(rand.nextInt(i + 10));
QueueDemo.printQ(priorityQueue);
List<Integer> ints = Arrays.asList(25, 22, 20,
18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25);
priorityQueue = new PriorityQueue<Integer>(ints);
QueueDemo.printQ(priorityQueue);
priorityQueue = new PriorityQueue<Integer>(
ints.size(), Collections.reverseOrder());
priorityQueue.addAll(ints);
QueueDemo.printQ(priorityQueue);
String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
List<String> strings = Arrays.asList(fact.split(""));
PriorityQueue<String> stringPQ =
new PriorityQueue<String>(strings);
QueueDemo.printQ(stringPQ);
stringPQ = new PriorityQueue<String>(
strings.size(), Collections.reverseOrder());
stringPQ.addAll(strings);
QueueDemo.printQ(stringPQ);
Set<Character> charSet = new HashSet<Character>();
for(char c : fact.toCharArray())
charSet.add(c); // Autoboxing
PriorityQueue<Character> characterPQ =
new PriorityQueue<Character>(charSet);
QueueDemo.printQ(characterPQ);
}
} /* Output:
0 1 1 1 1 1 3 5 8 14
1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25
25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1
A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W
W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A
A B C D E F H I L N O S T U W
*///:~

12. Collection和Iterator

Collection是描述所有序列容器的共性的根接口,它可能会被认为是一个”附属接口”,即因为要表示其他若干个接口的共性而出现的接口.另外,java.util.AbstractCollection类提供了Collection的默认实现,使得你可以创建AbstractCollection的子类型,而其中没有不必要的代码重复.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//: holding/InterfaceVsIterator.java
import typeinfo.pets.*;
import java.util.*;
public class InterfaceVsIterator {
public static void display(Iterator<Pet> it) {
while(it.hasNext()) {
Pet p = it.next();
System.out.print(p.id() + ":" + p + " ");
}
System.out.println();
}
public static void display(Collection<Pet> pets) {
for(Pet p : pets)
System.out.print(p.id() + ":" + p + " ");
System.out.println();
}
public static void main(String[] args) {
List<Pet> petList = Pets.arrayList(8);
Set<Pet> petSet = new HashSet<Pet>(petList);
Map<String,Pet> petMap =
new LinkedHashMap<String,Pet>();
String[] names = ("Ralph, Eric, Robin, Lacey, " +
"Britney, Sam, Spot, Fluffy").split(", ");
for(int i = 0; i < names.length; i++)
petMap.put(names[i], petList.get(i));
display(petList);
display(petSet);
display(petList.iterator());
display(petSet.iterator());
System.out.println(petMap);
System.out.println(petMap.keySet());
display(petMap.values());
display(petMap.values().iterator());
}
} /* Output:
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
4:Pug 6:Pug 3:Mutt 1:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
4:Pug 6:Pug 3:Mutt 1:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat
{Ralph=Rat, Eric=Manx, Robin=Cymric, Lacey=Mutt, Britney=Pug, Sam=Cymric, Spot=Pug, Fluffy=Manx}
[Ralph, Eric, Robin, Lacey, Britney, Sam, Spot, Fluffy]
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
*///:~
  • Foreach与迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//: holding/ForEachCollections.java
// All collections work with foreach.
import java.util.*;
public class ForEachCollections {
public static void main(String[] args) {
Collection<String> cs = new LinkedList<String>();
Collections.addAll(cs,
"Take the long way home".split(" "));
for(String s : cs)
System.out.print("'" + s + "' ");
}
} /* Output:
'Take' 'the' 'long' 'way' 'home'
*///:~

下面的例子能显示所有操作系统的环境变量:

1
2
3
4
5
6
7
8
9
10
11
//: holding/EnvironmentVariables.java
import java.util.*;
public class EnvironmentVariables {
public static void main(String[] args) {
for(Map.Entry entry: System.getenv().entrySet()) {
System.out.println(entry.getKey() + ": " +
entry.getValue());
}
}
} /* (Execute to see output) *///:~

13.1 适配器方法惯用法

一种习惯用的方法是适配器方法的惯用法.”适配器”部分来自于设计模式,因为你必须提供特定接口以满足foreach语句.当你有一个接口并需要另外一个接口时,编写适配器就可以解决问题.这里,在默认的前向迭代器的基础上,添加产生反向迭代器的能力.而是添加一个能够产生Iterable对象的方法,该对象可以用于foreach语句.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//: holding/AdapterMethodIdiom.java
// The "Adapter Method" idiom allows you to use foreach
// with additional kinds of Iterables.
import java.util.*;
class ReversibleArrayList<T> extends ArrayList<T> {
public ReversibleArrayList(Collection<T> c) { super(c); }
public Iterable<T> reversed() {
return new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
int current = size() - 1;
public boolean hasNext() { return current > -1; }
public T next() { return get(current--); }
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
};
}
}
public class AdapterMethodIdiom {
public static void main(String[] args) {
ReversibleArrayList<String> ral =
new ReversibleArrayList<String>(
Arrays.asList("To be or not to be".split(" ")));
// Grabs the ordinary iterator via iterator():
for(String s : ral)
System.out.print(s + " ");
System.out.println();
// Hand it the Iterable of your choice
for(String s : ral.reversed())
System.out.print(s + " ");
}
} /* Output:
To be or not to be
be to not or be To
*///:~

如果直接将ral对象置于foreach语句中,将得到默认的前向迭代器.但是如果在该对象上调用reversed()方法,就会产生不同的行为.

    1. 总结

Java提供了大量持有对象的方式 :

  • 数组将数字与对象联系起来.它保存类型明确的对象,查询对象时,不需要对结果做类型转换.它可以是多维的,可以保存基本类型的数据.但是,数组一旦生成,其容量就不可改变.
  • Collection保存单一的元素,而Map保存相关联的键值对.有了Java的泛型,就可以指定容器中存放的对象类型,因此不会将错误的类型放入到泛型中.容器不能持有基本类型,但是自动包装机制会仔细地执行基本类型到容器中所持有的包装器类型之间的双向转换.
  • 像数组一样,List也建立数字索引与对象的关联,因此,数组和List都是排好序的容器.List能够自动扩充容量.
  • 如果要进行大量的随机访问,就使用ArrayList,如果要经常从表中插入和删除元素,则需要使用LinkedList.
  • 各种Queue以及栈的行为,由LinkedList提供支持.
  • Map是一种将对象与对象相关联的设计.HaspMap设计用来快速访问,而TreeMap保存”键”始终处于排序状态,所以没有HashMap快.LinkedHashMap保存元素插入的顺序,也是通过散列提供了快速访问的能力.
  • Set不接受重复元素.HashSet提供最快的查询速度,而TreeSet保存元素处于排序的状态.LinkedHashSet以插入顺序保存元素.

简单的容器分类