前面介紹完了幾個Collections Framework的幾個主要接口,下面來講一下對象是如何排序的,為下面的SortedSet和SortedMap做準備。
舉個例子,我們有個list,里面存儲了若干個String,那么我們可以用下面的方法來對list內部的String按照字母順序來排序:
Collections.sort(list);
那如果list里存放的不是String,而是其他類型的對象呢?能否排序就取決于該對象是否實現了Comparable這個接口,如果沒有的話,Collections.sort()方法就將拋出ClassCastExcepton。
下面的表格列出了java里實現了Comparable接口的一些重要的類:
Classes Implementing
Comparable
Class |
Natural Ordering |
Byte |
Signed numerical |
Character |
Unsigned numerical |
Long |
Signed numerical |
Integer |
Signed numerical |
Short |
Signed numerical |
Double |
Signed numerical |
Float |
Signed numerical |
BigInteger |
Signed numerical |
BigDecimal |
Signed numerical |
Boolean |
Boolean.FALSE < Boolean.TRUE |
File |
System-dependent lexicographic on path name |
String |
Lexicographic |
Date |
Chronological |
CollationKey |
Locale-specific lexicographic |
如果你想對自己的類實現Comparable接口,一定要先參考API文檔關于Comparable接口的說明。
另外,如果你想用自己的排序屏蔽原本的排序方式,又或者你想對某個類的對象排序,但該類并沒有實現Comparable接口,這個時候,你就可以用Comparator接口來實現你自己的排序方式。
但有一點要注意,排序也不是亂排的,應該盡量和類方法中的equals方法保持一致,尤其是當Comparator用在SortedSet或者SortedMap中的時候(這兩個可以通過指定Comparator來覆蓋自然排序方式)。
舉個例子來說:
public class Cmp {
/**
* @param args
*/
public static void main(String[] args) {
SortedSet set = new TreeSet();
set.add("a");
set.add("a");
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
我們知道Set是不允許有重復元素的,所以即使我們加入了兩個“a”,最后輸出的結果仍然是只有一個“a”。
但如果我們對程序做如下修改,指定自己的Comparator:
public class Cmp {
static final Comparator cp = new Comparator() {
public int compare(Object o1, Object o2) {
return 1;
}
};
/**
* @param args
*/
public static void main(String[] args) {
SortedSet set = new TreeSet(cp);
set.add("a");
set.add("a");
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
這次輸出的結果將是兩個"a",這與Set本身的規范并不相符。
6. SortedSet
SortedSet一種按升序來排列元素的Set。排序的規則是按照元素的自然順序,或是按照構造函數中指定的Comparator規定的順序。
下面是SortedSet的定義:
public interface SortedSet<E> extends Set<E> {
// Range-view
SortedSet<E> subSet(E fromElement, E toElement);
SortedSet<E> headSet(E toElement);
SortedSet<E> tailSet(E fromElement);
// Endpoints
E first();
E last();
// Comparator access
Comparator<? super E> comparator();
}
我們看到,SortedSet提供了一些特殊的操作:
Range view
— allows arbitrary range operations on the sorted
set
Endpoints
— returns the first or last element in the sorted set
Comparator access
— returns the Comparator
, if
any, used to sort the set
至于其他的從Set繼承過來的操作,基本都和Set一樣。之所以說“基本”,是因為有兩個例外:
·SortedSet得到的iterator是按順序進行遍歷的(Set沒有順序)
·toArray得到的數組是按序排列SortedSet中的元素的(Set沒有順序)
SortedSet (Java 2 Platform SE 6)
所有通用有序 set 實現類都應該提供 4 個“標準”構造方法:
1) void(無參數)構造方法,它創建一個空的有序
set,按照元素的自然順序進行排序。
2) 帶有一個 Comparator 類型參數的構造方法,它創建一個空的有序
set,根據指定的比較器進行排序。
3) 帶有一個 Collection 類型參數的構造方法,它創建一個新的有序
set,其元素與參數相同,按照元素的自然順序進行排序。
4) 帶有一個 SortedSet 類型參數的構造方法,它創建一個新的有序
set,其元素和排序方法與輸入的有序 set 相同。如何做到這一點?別忘了SortedSet本身就有一個comparator()方法,可以返回他所用的Comparator。但要注意的是無法保證強制實施此建議,因為接口不能包含構造方法。
在介紹List的時候,我們曾經強調,List里的range-view方法List<E> subList(int from, int to);是要嚴格避免濫用的。因為在subList的作用域范圍內,任何對list本身的修改都將使subList的行為不可預期。
但是在SortedSet里情況就不一樣了,你應該注意到,SortedSet里的三個range-view方法,都是根據提供的比較范圍來截取subSet,而不是像List里那樣使用索引。這樣在subSet的作用域范圍內,對set本身的修改都將反映到subSet上,反之亦然。參見下面的例子:
public class Cmp {
/**
* @param args
*/
public static void main(String[] args) {
// SortedSet set = new TreeSet(cp);
SortedSet set = new TreeSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
SortedSet sub = set.tailSet("c");
Iterator it = sub.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
set.add("f");
System.out.println("-------------------");
it = sub.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
輸出的結果是:
c
d
e
-------------------
c
d
e
f
可以看到,我們得到“值大于等于c”的subSet后,當我們再向原來的set加入一個新的元素“f”,由于這個元素也大于“c”,那么這個元素也將反映到subSet上。
下面介紹一個比較特殊的小技巧:通過使用subSet,如何得到一個閉包的結果?
我們知道,如果按照一般的調用,下面的代碼是獲得大于等于“doorbell”而小于“pickle”的subSet:
dictionary.subSet("doorbell", "pickle");
而我們可以通過下面方法得到大于等于“doorbell”而
小于等于“pickle”的subSet:
dictionary.subSet("doorbell", "pickle\0");
同理,我們可以通過下面方法得到
大于“doorbell”而
小于“pickle”的subSet:
dictionary.subSet("doorbell\0", "pickle");
7. SortedMap
SortedMap是對key進行升序排列的Map。
下面是其定義:
public interface SortedMap<K, V> extends Map<K, V>{
Comparator<? super K> comparator();
SortedMap<K, V> subMap(K fromKey, K toKey);
SortedMap<K, V> headMap(K toKey);
SortedMap<K, V> tailMap(K fromKey);
K firstKey();
K lastKey();
}
和SortedSet類似,SortedMap也提供了如下一些額外的操作:
Range view
— performs arbitrary range operations on the sorted
map
Endpoints
— returns the first or the last key in the sorted map
Comparator access
— returns the Comparator
, if
any, used to sort the map
·SortedMap得到的iterator是按順序進行遍歷的(Map沒有順序)
·toArray得到的數組是按序排列SortedMap中的元素的(Map沒有順序)
SortedMap (Java 2 Platform SE 6)
注意,如果有序映射要正確實現 Map 接口,則有序映射所維持的順序(無論是否提供了明確的比較器)都必須與 equals
一致。(有關與 equals 一致 的精確定義,請參閱 Comparable 接口或
Comparator 接口)。這是因為 Map 接口是按照 equals 操作定義的,但有序映射使用它的
compareTo(或
compare)方法對所有鍵進行比較,因此從有序映射的角度來看,此方法認為相等的兩個鍵就是相等的。即使順序與 equals
不一致,樹映射的行為仍然是 定義良好的,只不過沒有遵守 Map 接口的常規協定。
所有通用有序映射實現類都應該提供 4 個“標準”構造方法:1) void(無參數)構造方法,它創建一個空的有序映射,按照鍵的自然順序進行排序。2)
帶有一個 Comparator 類型參數的構造方法,它創建一個空的有序映射,根據指定的比較器進行排序。3) 帶有一個 Map
類型參數的構造方法,它創建一個新的有序映射,其鍵-值映射關系與參數相同,按照鍵的自然順序進行排序。4) 帶有一個 SortedMap
類型參數的構造方法,它創建一個新的有序映射,其鍵-值映射關系和排序方法與輸入的有序映射相同。無法保證強制實施此建議,因為接口不能包含構造方法。
總結
The Java Collections Framework hierarchy consists of two distinct interface
trees:
- The first tree starts with the
Collection
interface, which
provides for the basic functionality used by all collections, such as
add
and remove
methods. Its subinterfaces —
Set
, List
, and Queue
— provide for more
specialized collections.
The Set
interface does not allow duplicate elements. This can be
useful for storing collections such as a deck of cards or student records. The
Set
interface has a subinterface, SortedSet
, that
provides for ordering of elements in the set.
The List
interface provides for an ordered collection, for
situations in which you need precise control over where each element is
inserted. You can retrieve elements from a List
by their exact
position.
The Queue
interface enables additional insertion, extraction,
and inspection operations. Elements in a Queue
are typically
ordered in on a FIFO basis.
- The second tree starts with the
Map
interface, which maps keys
and values similar to a Hashtable
.
Map
's subinterface, SortedMap
, maintains its
key-value pairs in ascending order or in an order specified by a
Comparator
.