.#完整的容器分類法
1.
填充容器
1.Collections類也有一些實用的用于填充的方法,其中包括fill()。與Arrays的一樣,也只是復制同一個對象引用來填充整個容器,并且只對List有用。
一.使用Abstract類
1.對于產(chǎn)生用于容器的測試數(shù)據(jù)問題,有一種方式是創(chuàng)建定制的Collection和Map實現(xiàn)。每個java.util容器都有自己的Abstract類,它們提供了該容器的部分實現(xiàn),因此你必須做的只是去實現(xiàn)那些產(chǎn)生想要容器所必需的方法。
Collection的功能方法
1.下面是可以通過Collection執(zhí)行的所有操作:
| Method | Description |
|---|---|
| boolean add(T) | 確保容器持有具有泛型類型T的參數(shù)。如果沒有將此參數(shù)添加進容器,則返回false |
| boolean addAll(Co?llection<? extends T>) | 添加參數(shù)中的所有元素。只要添加了任意元素就返回true |
| void clear() | 移除容器中的所有元素 |
| boolean contains(T) | 如果容器已持有具有泛型類型T的詞參數(shù),則返回true |
| boolean containsAll(Collection<?>) | 如果容器持有此參數(shù)中的所有元素,則返回true |
| boolean isEmpty() | 容器中沒有元素時返回true |
| Iterator<T> iterator() | 返回一個Iterator<T>,可以用來遍歷容器中的元素 |
| boolean remove(Object) | 如果參數(shù)在容器中,則移除此元素的一個實例。如果做了移除動作,則返回true |
| boolean removeAll(Collection<?>) | 移除參數(shù)中的所有元素。只要有移除動作發(fā)生就返回true |
| boolean retainAll(Collection<?>) | 只保存參數(shù)中的元素(即“交集”)。只要Collection發(fā)生改變就返回true |
| int size() | 返回容器中的元素的數(shù)目 |
| Object[] toArray() | 返回一個數(shù)組,該數(shù)組包含容器中的所有元素 |
| <T> T[] toArray(T[] a) | 返回一個數(shù)組,該數(shù)組包含容器中的所有元素。返回結(jié)果的運行時類型與參數(shù)數(shù)組a的類型相同,而不是單純的Object |
2.由于Collection包括Set,因此,如果想檢查Collection中的元素,那就必須使用迭代器。
可選操作
1.執(zhí)行各種不同的添加和移除的方法在Collection接口中都是可選操作。這意味著實現(xiàn)類并不需要為這些方法提供功能定義。
2.將方法定義為可選是因為這樣做可以防止在設計中出現(xiàn)接口爆炸的情況。
3.“未獲支持的操作”這種方式可以實現(xiàn)Java容器類庫的一個重要目標:容器應該易學易用。未獲支持的操作是一種特例,可以延遲到需要時再實現(xiàn)。但是為了讓這種方式工作:
(1)UnsupportedOperationException必須是一種罕見事件。即,對大多數(shù)類來說,所有操作都應該可以工作,只有在特例中才會有未獲支持操作。這種設計意味著,在Java容器類庫中,如果想要創(chuàng)建新的Collection,但是沒有為Collection接口中的所有方法都提供有意義的定義,那么它仍舊適合現(xiàn)有的類庫。
(2)如果一個操作是未支持的,那么在實現(xiàn)接口的時候就會導致UnsupportedOperationException異常。
一.未獲支持操作
1.最常見的未獲支持操作,都來源于背后由固定尺寸的數(shù)據(jù)結(jié)構(gòu)支持的容器。
public class Unsupported {
public static void test(String msg, List<String> list) {
Collection<String> c = list;
Collection<String> subList = list.subList(1, 8);
Collection<String> c2 = new ArrayList<String>(subList);
System.out.println("---" + msg + "---");
try {
c.retainAll(c2);
} catch (Exception e) {
System.out.println("retainAll():" + e);
}
try {
c.removeAll(c2);
} catch (Exception e) {
System.out.println("removeAll():" + e);
}
try {
c.clear();
} catch (Exception e) {
System.out.println("clear():" + e);
}
try {
c.add("X");
} catch (Exception e) {
System.out.println("add():" + e);
}
try {
c.addAll(c2);
} catch (Exception e) {
System.out.println("addAll():" + e);
}
try {
c.remove("C");
} catch (Exception e) {
System.out.println("remove():" + e);
}
try {
list.set(0, "X");
} catch (Exception e) {
System.out.println("List.set():" + e);
}
}
public static void main(String...args) {
List<String> list = Arrays.asList(("A B C D E F").split(" "));
test("Arrays.asList()", list);
test("unmodifiableList()", Collection.unmodifiableList(new ArrayList(list)));
}
}
/* Output:
---Arrays.asList()---
retainAll():java.lang.UnsupportedOperationException
removeAll():java.lang.UnsupportedOperationException
clear():java.lang.UnsupportedOperationException
add():java.lang.UnsupportedOperationException
addAll():java.lang.UnsupportedOperationException
remove():java.lang.UnsupportedOperationException
---unmodifiableList()---
retainAll():java.lang.UnsupportedOperationException
removeAll():java.lang.UnsupportedOperationException
clear():java.lang.UnsupportedOperationException
add():java.lang.UnsupportedOperationException
addAll():java.lang.UnsupportedOperationException
remove():java.lang.UnsupportedOperationException
List.set():java.lang.UnsupportedOperationException
*/
上面的代碼中,Arrays.asList()會生成一個List,它基于一個固定大小的數(shù)組,僅支持那些不會改變數(shù)組大小的操作,因此除了set()方法,其它方法都會拋出異常。Collections.unmodifiableList()產(chǎn)生的是一個不可修改的列表,因此任意方法都不能對它產(chǎn)生修改。
List的功能方法
1.基本的List很容易使用:大多數(shù)的時候只是調(diào)用add()添加對象,使用get()一次取出一個元素,以及調(diào)用iterator()獲取用于該序列的Iterator。
Set和存儲順序
1.在Java中有諸如Integer和String這樣的預定義類型,這些類型被定義為可依在容器內(nèi)部使用。當創(chuàng)建自己的類型時,要意識到Set需要一種方式來維護存儲順序,而存儲順序如何維護,則是在Set的不同實現(xiàn)之間會有所變化。
2.不同Set實現(xiàn)不僅具有不同當行為,而且它們對于放置在特定的Set中的元素類型也有不同的要求:
| Set類型 | 要求 |
|---|---|
| Set | 存入Set的每個元素都必須是唯一的,因為Set不重復保存元素。加入Set的元素必須定義equals()方法以確保對象的唯一性。Set與Collection有完全一樣的接口。Set接口不保證維護元素的次序 |
| HashSet | 為快速查找而設計的Set。存入HashSet的元素必須定義HashCode() |
| TreeSet | 保持次序的Set,底層為樹結(jié)構(gòu)。使用它可以從Set中提取有序的序列。元素必須事項Comparable接口 |
| LinkedHashSet | 具有HashSet的查詢速度,且內(nèi)部使用鏈表維護元素的順序(插入的順序)。于是在使用迭代器遍歷Set時,結(jié)果會按元素插入的次序顯示,元素也必須定義hashCode()方法 |
3.如果沒有其他限制,HashSet就應該是你默認的選擇,因為它對速度進行了優(yōu)化。
4.通常你必須為散列存儲和樹型存儲都創(chuàng)建一個equals()方法,但是hashCode()只有在這個類將會被置于HashSet或者LinkedHashSet中市=時才是必需的。你應該在覆蓋equals()方法的同時覆蓋hashCode()方法,定義hashCode()的機制將會在后面介紹:
class SetType {
int i;
public SetType(int n) {
i = n;
}
public boolean equals(Object o) {
return o instanceof SetType && (i == (SetType) o).i;
}
public String toString() {
return Integer.toString(i);
}
}
class HashType extends SetType {
public HashType(int n) {
super(n);
}
public int hashCode() {
return i;
}
}
class TreeType extends SetType implements Comparable<TreeType> {
public TreeType(int n) {
super(n);
}
public compareTo(TreeType arg) {
return (arg.i < i ? -1 : (arg.i == i ? 0 : 1));
}
}
上面的代碼展示了編寫存儲類型的最基本的方式,HashType放置于HashSet中,TreeType放置于TreeSet中。
5.如果我們嘗試著將沒有恰當支持必需操作的類型用于需要這些方法的Set,并不會出現(xiàn)任何問題,甚至不會出現(xiàn)運行時異常,那是因為hashCode()和equals()的默認實現(xiàn)是合法的,即便它不正確。但是如果這樣就將它們放置到任何散列實現(xiàn)中都會實現(xiàn)重復,這樣就違反了Set的基本契約。
一.SortedSet
1.SortedSet中的元素可以保證處于排序狀態(tài),也就賦予了它接口外的其他功能:
| Method | 功能描述 |
|---|---|
| Comparator comparator() | 返回當前Set使用的Comparator;或者返回null,表示以自然的方式排序 |
| Object first() | 返回容器中第一個元素 |
| Object last() | 返回容器中最后一個元素 |
| SortedSet subSet(fromElement, toElement) | 生成此Set的子集,范圍從fromElement(包含)到toElement(不包含) |
| SortedSet headSet(toElement) | 生成此Set的子集,由小于toElement的元素組成 |
| SortedSet tailSet(fromElement) | 生成此Set的子集,由大于或等于fromElement的元素組成 |
隊列
1.除了并發(fā)應用,Queue在Java SE5中僅有的兩個實現(xiàn)是LinkedList和PriorityQueue,它們的差異在于排序行為而不是性能:
class QueueBehavior {
private static int count = 10;
static <T> void test(Queue<T> queue, Generator<T> gen) {
for (int i = 0; i < count; i++)
queue.offer(gen.next());
while (queue.peek() != null)
System.out.print(queue.remove() + " ");
System.out.println();
}
static class Gen implements Generator<String> {
String[] s = ("one two three four five six seven eight nine ten").split(" ");
int i;
public String next() {
return s[i++];
}
}
public static void main(String... args) {
test(new LinkedList<String>(), new Gen());
test(new PriorityQueue<String>(), new Gen());
test(new ArrayBlockingQueue<String>(count), new Gen());
test(new ConcurrentLinkedList<String>(), new Gen());
test(new LinkedBlockingQueue<String>(), new Gen());
test(new PriorityBlockingQueue<String>(), new Gen());
}
}
/*
Output:
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
*/
一.優(yōu)先級隊列
1.優(yōu)先級隊列聲明下一個彈出的元素是最需要的元素(具有最高級的優(yōu)先級)。
2.當你在PriorityQueue上調(diào)用offer()方法來插入一個對象時,這個對象會在隊列中被排序。默認的排序?qū)⑹褂脤ο笤陉犃兄械淖匀豁樞?,但是可以容果提供自己?strong>Comparator來修改這個順序。
3.在這里,重復時允許的,最小的值擁有組最高的優(yōu)先級。
二.雙向隊列
1.雙向隊列就像是一個隊列,但是你可以在任何一段添加或移除元素。
2.Java標準類庫中沒有人和顯示的用于雙向隊列的接口。但是,可以使用組合來創(chuàng)建一個Deque類,并直接從LinkedList中暴露相關(guān)方法:
public class Deque<T> {
private LinkedList<T> deque = new LinkedList<T>;
public void addFirst(T e) { deque.addFirst(e); }
public void addLast(T e) { deque.addLast(e); }
public T getFirst() { return deque.getFirst(); }
public T getLast() { return deque.getLast(); }
public T removeFirst() { return deque.removeFirst(); }
public T removeLast() { return deque.removeLast(); }
public int size() { rerturn deque.size(); }
}