java中提供了一套相當完整的集合類,其中基本的類型是List,Set,Queue和Map。
11.1 泛型和類型安全的容器
ArrayList<Apple>,不僅僅只是ArrayList,其中尖括號括起來的是類型參數(可以有多個),它指定了這個容器實例可以保存的類型。通過使用泛型,就可以在編譯器防止將錯誤類型的對象放置到容器中。向上轉型也可以像作用于其他類型一樣作用于泛型。
11.3 添加一組元素
Collection.addAll()
Collections.addAll()
兩者的區別:前者只能接受另一個Collection對象作為參數,后者是可變參數,且第一個參數是被添加的Collection對象。
Arrays.asList,其底層表示的是數組,因此不能調整尺寸。
//: holding/AsListInference.java
// Arrays.asList() makes its best guess about type.
import java.util.*;
class Snow {}
class Powder extends Snow {}
class Light extends Powder {}
class Heavy extends Powder {}
class Crusty extends Snow {}
class Slush extends Snow {}
public class AsListInference {
public static void main(String[] args) {
List<Snow> snow1 = Arrays.asList(
new Crusty(), new Slush(), new Powder());
// Won't compile:
// List<Snow> snow2 = Arrays.asList(
// new Light(), new Heavy());
// Compiler says:
// found : java.util.List<Powder>
// required: java.util.List<Snow>
// Collections.addAll() doesn't get confused:
List<Snow> snow3 = new ArrayList<Snow>();
Collections.addAll(snow3, new Light(), new Heavy());
// Give a hint using an
// explicit type argument specification:
List<Snow> snow4 = Arrays.<Snow>asList(
new Light(), new Heavy());
}
} ///:~
Arrays.asList()只有Powder類型,因此它會創建List<Powder>而不是List<Snow>。
Collections.addAll工作的很好,因為它從第一個參數中了解到了目標類型是什么。
Arrays.<Snow>asList告訴了編譯器實際的目標類型應該是什么。這稱為顯示類型參數說明。
TreeSet, TreeMap,按照升序保存。
LinkedHashSet, LinkedHashMap,按照插入順序保存,同時保留了查詢速度。
11.5 List
有兩種list:
基本的ArrayList,優點是隨機訪問元素,但是在List的中間插入和刪除元素時較慢;
LinkedList,通過代價較低的在List中間進行的插入和刪除操作,提供了優化的順序訪問,LinkedList在隨機訪問方面相對比較慢。
Collections類的shuffle()方法的作用是將List中的內容隨機打亂順序。
11.6 迭代器
迭代器不必知道序列底層的機構,通常被稱為輕量級對象,創建它的代價小。迭代器統一了對容器的訪問方式。
ListIterator是一個更加強大的Iterator子類型,它只能用于各種List類的訪問。
//: 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]
*///:~
ListIterator可以雙向移動,獲取當前位置的前后元素索引,set()方法替換它訪問過的最后一個元素,通過調用ListIterator(n)方法創建一個一開始就指向列表索引為n的元素處得ListIterator。
11.7 LinkedList
//: 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
*///:~
11.9 Set
查找成為了Set中最重要的操作,通常會選擇一個HashSet的實現,它專門對快速查找進行了優化。
在TreeSet的構造器里可以傳入比較器。
11.10 Map
使用containsKeys()和containsValue()來測試一個Map,以便查看它是否包含某個鍵某個值。
Map的值可以是其他容器
entrySet()返回一個實現Map.Entry接口的對象集合。Entry是Map中用來保存一個鍵值對的,而Map實際上就是多個Entry的集合。
11.11 Queue
LinkedList提供了方法以支持隊列的行為,并且它實現了Queue接口,因此LinkedList可以用作Queue的一種實現。通過將LinkedList向上轉型為Queue。
offer方法將一個元素插入到隊尾,或者返回false。peek和element都是不移除的情況下返回隊頭,peek在隊空時返回null。poll和remove方法將移除并返回隊頭,poll在空的時候返回null。
自動裝箱機制可以將int轉換成queue所需的Interger對象。
優先級隊列聲明下一個彈出元素時最需要的元素(具有最高的優先級)。
PriorityQueue上調用offer方法來插入一個對象時,這個對象會在隊列中被排序。某認的排序將使用對象在隊列中的自然順序,但是你可以通過提供自己的Comparator來修改這個順序。PriorityQueue可以確保當你調用peek、poll、remove方法時,獲取的元素將是隊列中優先級最高的元素。
//: 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
*///:~
最小值擁有最高的優先級,空格比子目的優先級更高。可以使用Comparator對象改變排序
11.12 Collection和Iterator
Collection是描述所有序列容器的共性的根接口,通過針對接口而非具體實現來編寫代碼,我們的代碼可以應用于更多的對象類型,與底層容器的特定實現解耦。
一共有三種方式實現可迭代:
- 繼承AbstractCollection;
- 實現Collection;
- 繼承并提供創建迭代器,生成Iterator。將隊列與消費隊列的方法連接在一起耦合度最小,并且與實現Collection相比,它在序列類上所施加的約束也少得多。
11.13 Foreach與迭代器
如果創建了任何實現Iterable的類,都可以將它用于foreach語句中。主要包括所有的Collection類,但是不包括各種Map。
foreach語句可以用于數組,這并不意味著數組肯定也是一個Iterable,不存在任何從數組到Iterable的自動轉換,必須手動執行這種轉換,Arrays.asList()。
Arrays.asList()和構造方法區別:
Arrays.asList()的輸出傳遞給了ArrayList的構造器,這將創建一個引用對象,因此打亂這些引用不會修改該數組;但是如果直接使用Arrays.asList(),產生的List對象會使用底層數組作為其物理實現,只要修改這個List,底層的數組也會隨之修改。
容器不能持有基本類型,但是自動包裝機制會仔細地執行基本類型到容器中所持有的包裝器類型之間的雙向轉換。
Queue和Stack的行為,有LinkedList提供支持。