yxhxj2006

          常用鏈接

          統(tǒng)計(jì)

          最新評(píng)論

          Java 集合框架

          在常見(jiàn)用法中,集合(collection)和數(shù)學(xué)上直觀的集(set)的概念是相同的。 集是一個(gè)唯一項(xiàng)組,也就是說(shuō)組中沒(méi)有重復(fù)項(xiàng)。實(shí)際上,“集合框架”包含了一個(gè) Set 接口和許多具體的 Set 類。但正式的集概念卻比 Java 技術(shù)提前了一個(gè)世紀(jì),那時(shí)英國(guó)數(shù)學(xué)家 George Boole 按邏輯正式的定義了集的概念。大部分人在小學(xué)時(shí)通過(guò)我們熟悉的維恩圖引入的“集的交”和“集的并”學(xué)到過(guò)一些集的理論。 

          集的一些現(xiàn)實(shí)的示例如下: 
          •大寫(xiě)字母集“A”到“Z” 
          •非負(fù)整數(shù)集{0, 1, 2 ...} 
          •保留的 Java 編程語(yǔ)言關(guān)鍵字集 {'import', 'class', 'public', 'protected'...} 
          •人集(friends, employees, clients, ...) 
          •數(shù)據(jù)庫(kù)查詢返回記錄集 
          •Container 的 Component 對(duì)象集 
          •所有對(duì)(pair)集 
          •空集{} 

          集的基本屬性如下: 
          •集內(nèi)只包含每項(xiàng)的一個(gè)實(shí)例 
          •集可以是有限的,也可以是無(wú)限的 
          •可以定義抽象概念 

          集不僅是邏輯學(xué)、數(shù)學(xué)和計(jì)算機(jī)科學(xué)的基礎(chǔ),對(duì)于商業(yè)和系統(tǒng)的日常應(yīng)用來(lái)說(shuō),它也很實(shí)用。“連接池”這一概念就是數(shù)據(jù)庫(kù)服務(wù)器的一個(gè)開(kāi)放連接集。Web 服務(wù)器必須管理客戶機(jī)和連接集。文件描述符提供了操作系統(tǒng)中另一個(gè)集的示例。 

          映射是一種特別的集。它是一種對(duì)(pair)集,每個(gè)對(duì)表示一個(gè)元素到另一元素的單向映射。一些映射示例有: 
          •IP 地址到域名(DNS)的映射 
          •關(guān)鍵字到數(shù)據(jù)庫(kù)記錄的映射 
          •字典(詞到含義的映射) 
          •2 進(jìn)制到 10 進(jìn)制轉(zhuǎn)換的映射 

          就像集一樣,映射背后的思想比 Java 編程語(yǔ)言早的多,甚至比計(jì)算機(jī)科學(xué)還早。集和映射是數(shù)學(xué)領(lǐng)域的重要工具,人們非常了解它們的屬性。不僅如此,人們?cè)缇驼J(rèn)識(shí)到用集和映射解決編程問(wèn)題是有效的。1969 年發(fā)明的一種名為 SETL(Set Language)的語(yǔ)言只包含 set 一種基本數(shù)據(jù)類型(SETL 也包含垃圾收集 ― 這項(xiàng)技術(shù)在 20 世紀(jì) 90 年代開(kāi)發(fā)了 Java 技術(shù)后才被人們普遍接受)。雖然連 C++ 在內(nèi)的許多語(yǔ)言都包含集和映射,但“集合框架”很可能是設(shè)計(jì)最完美的集和映射包,并且是為流行的語(yǔ)言而寫(xiě)的。 (C++ 標(biāo)準(zhǔn)模板庫(kù)(Standard Template Library(STL))和 Smalltalk 的集合層次結(jié)構(gòu)的用戶對(duì)最后一點(diǎn)可能有爭(zhēng)議。) 

          此外,因?yàn)橛成湟彩羌运鼈兛梢允怯邢薜模部梢允菬o(wú)限的。 無(wú)限映射的一個(gè)示例如 2 進(jìn)制到 10 進(jìn)制的轉(zhuǎn)換。不幸的是,“集合框架”不支持無(wú)限映射 ― 有時(shí)用數(shù)學(xué)函數(shù)、公式或算法更好。但在有限映射能解決問(wèn)題時(shí),“集合框架”會(huì)給 Java 程序員提供一個(gè)有用的 API。 

          因?yàn)?#8220;集合框架”有類 Set、Collection 和 Map 的正規(guī)定義,您會(huì)注意到小寫(xiě)的詞 set、collection 和 map 把實(shí)現(xiàn)和概念區(qū)分開(kāi)來(lái)。 

          集合接口和類 

          既然您已經(jīng)具備了一些集的理論,您應(yīng)該能夠更輕松的理解“集合框架”。 “集合框架”由一組用來(lái)操作對(duì)象的接口組成。不同接口描述不同類型的組。 在很大程度上,一旦您理解了接口,您就理解了框架。 雖然您總要?jiǎng)?chuàng)建接口特定的實(shí)現(xiàn),但訪問(wèn)實(shí)際集合的方法應(yīng)該限制在接口方法的使用上;因此,允許您更改基本的數(shù)據(jù)結(jié)構(gòu)而不必改變其它代碼。框架接口層次結(jié)構(gòu)如下圖所示。 

          有的人可能會(huì)認(rèn)為 Map 會(huì)繼承 Collection。 在數(shù)學(xué)中,映射只是對(duì)(pair)的集合。但是,在“集合框架”中,接口 Map 和 Collection 在層次結(jié)構(gòu)沒(méi)有任何親緣關(guān)系,它們是截然不同的。這種差別的原因與 Set 和 Map 在 Java 庫(kù)中使用的方法有關(guān)。Map 的典型應(yīng)用是訪問(wèn)按關(guān)鍵字存儲(chǔ)的值。它支持一系列集合操作的全部,但操作的是鍵-值對(duì),而不是單個(gè)獨(dú)立的元素。因此 Map 需要支持 get() 和 put() 的基本操作,而 Set 不需要。此外,還有返回 Map 對(duì)象的 Set 視圖的方法: 
          Set set = aMap.keySet(); 
          用“集合框架”設(shè)計(jì)軟件時(shí),記住該框架四個(gè)基本接口的下列層次結(jié)構(gòu)關(guān)系會(huì)有用處: 
          •Collection 接口是一組允許重復(fù)的對(duì)象。 
          •Set 接口繼承 Collection,但不允許重復(fù)。 
          •List 接口繼承 Collection,允許重復(fù),并引入位置下標(biāo)。 
          •Map 接口既不繼承 Set 也不繼承 Collection。 

          讓我們轉(zhuǎn)到對(duì)框架實(shí)現(xiàn)的研究,具體的集合類遵循命名約定,并將基本數(shù)據(jù)結(jié)構(gòu)和框架接口相結(jié)合。除了四個(gè)歷史集合類外,Java 2 框架還引入了六個(gè)集合實(shí)現(xiàn),如下表所示。關(guān)于歷史集合類如何轉(zhuǎn)換、比如說(shuō),如何修改 Hashtable并結(jié)合到框架中,請(qǐng)參閱歷史集合類 。 

          接口 實(shí)現(xiàn) 歷史集合類 
          Set HashSet 
          TreeSet 
          List ArrayList Vector 
          LinkedList Stack 
          Map HashMap Hashtable 
          TreeMap Properties 

          這里沒(méi)有 Collection 接口的實(shí)現(xiàn)。歷史集合類,之所以這樣命名是因?yàn)閺?Java 類庫(kù) 1.0 發(fā)行版就開(kāi)始沿用至今了。 

          如果從歷史集合類轉(zhuǎn)換到新的框架類,主要差異之一在于所有的操作都和新類不同步。您可以往新類中添加同步的實(shí)現(xiàn),但您不能把它從舊的類中除去。 


          -------------------------------------------------------------------------------- 


          集合接口 

          Collection 接口用于表示任何對(duì)象或元素組。想要盡可能以常規(guī)方式處理一組元素時(shí),就使用這一接口。這里是以統(tǒng)一建模語(yǔ)言(Unified Modeling Language(UML))表示法表示的 Collection 公有方法清單。 

          該接口支持如添加和除去等基本操作。設(shè)法除去一個(gè)元素時(shí),如果這個(gè)元素存在,除去的僅僅是集合中此元素的一個(gè)實(shí)例。 
          •boolean add(Object element) 
          •boolean remove(Object element) 

          Collection 接口還支持查詢操作: 
          •int size() 
          •boolean isEmpty() 
          •boolean contains(Object element) 
          •Iterator iterator() 

          Iterator 接口 

          Collection 接口的 iterator() 方法返回一個(gè) Iterator。Iterator 和您可能已經(jīng)熟悉的 Enumeration接口類似,我們將在Enumeration 接口中對(duì) Enumeration 進(jìn)行討論。使用 Iterator 接口方法,您可以從頭至尾遍歷集合,并安全的從底層 Collection 中除去元素: 

          remove() 方法可由底層集合有選擇的支持。當(dāng)?shù)讓蛹险{(diào)用并支持該方法時(shí),最近一次 next() 調(diào)用返回的元素就被除去。為演示這一點(diǎn),用于常規(guī) Collection 的 Iterator 接口代碼如下: 
          Collection collection = ...; 
          Iterator iterator = collection.iterator(); 
          while (iterator.hasNext()) { 
          Object element = iterator.next(); 
          if (removalCheck(element)) { 
          iterator.remove(); 



          組操作 

          Collection 接口支持的其它操作,要么是作用于元素組的任務(wù),要么是同時(shí)作用于整個(gè)集合的任務(wù)。 
          •boolean containsAll(Collection collection) 
          •boolean addAll(Collection collection) 
          •void clear() 
          •void removeAll(Collection collection) 
          •void retainAll(Collection collection) 

          containsAll() 方法允許您查找當(dāng)前集合是否包含了另一個(gè)集合的所有元素,即另一個(gè)集合是否是當(dāng)前集合的子集。其余方法是可選的,因?yàn)樘囟ǖ募峡赡懿恢С旨细摹ddAll() 方法確保另一個(gè)集合中的所有元素都被添加到當(dāng)前的集合中,通常稱為并。clear() 方法從當(dāng)前集合中除去所有元素。removeAll() 方法類似于 clear(),但只除去了元素的一個(gè)子集。retainAll() 方法類似于 removeAll() 方法,不過(guò)可能感到它所做的與前面正好相反:它從當(dāng)前集合中除去不屬于另一個(gè)集合的元素,即交。 

          剩下的兩種將 Collection轉(zhuǎn)換成數(shù)組的接口方法,將在新集合到歷史集合的轉(zhuǎn)換中討論。 

          AbstractCollection 類 

          AbstractCollection 類提供具體“集合框架”類的基本功能。雖然您可以自行實(shí)現(xiàn) Collection 接口的所有方法,但是,除了iterator() 和 size() 方法在恰當(dāng)?shù)淖宇愔袑?shí)現(xiàn)以外,其它所有方法都由 AbstractCollection 類來(lái)提供實(shí)現(xiàn)。如果子類不覆蓋某些方法,可選的如 add() 之類的方法將拋出異常。 

          “集合框架”設(shè)計(jì)的相關(guān)事項(xiàng) 

          在創(chuàng)建“集合框架”的過(guò)程中,我們需要 Sun 開(kāi)發(fā)小組提供用于操作元素組的靈活接口。 為了使設(shè)計(jì)簡(jiǎn)單,我們不為每個(gè)可選的功能分別提供獨(dú)立的接口,而是提供了一個(gè)定義所有方法的接口。而這些方法是一個(gè)實(shí)現(xiàn)類才有可能提供的。然而,有些接口方法是可選的。因?yàn)橐粋€(gè)接口實(shí)現(xiàn)必須實(shí)現(xiàn)所有接口方法,調(diào)用程序就需要一種途徑來(lái)知道一個(gè)可選的方法是不是不受支持。調(diào)用一種可選方法時(shí),框架開(kāi)發(fā)小組所選擇去通知調(diào)用程序的方式是拋出一個(gè) UnsupportedOperationException。 如果在使用集合的過(guò)程中,一個(gè) UnsupportedOperationException 被拋出,則操作失敗,因?yàn)樗皇苤С帧nsupportedOperationException 類繼承 RuntimeException 類避免了不得不把所有集合操作放入 try-catch 塊。 

          除了借助運(yùn)行時(shí)異常處理可選操作外,具體集合實(shí)現(xiàn)的迭代器還是 故障快速(fail-fast)的。這意味著,當(dāng)另一個(gè)線程修改底層集合的時(shí)候,如果您正在用 Iterator 遍歷集合,那么,Iterator就會(huì)拋出 ConcurrentModificationException (另一種 RuntimeException)異常立刻失敗。就是說(shuō),下次調(diào)用 Iterator 方法時(shí)底層集合已修改過(guò)了,ConcurrentModificationException 異常就拋出了。 

          -------------------------------------------------------------------------------- 

          Set 接口 

          按照定義,Set 接口繼承 Collection 接口,而且它不允許集合中存在重復(fù)項(xiàng)。所有原始方法都是現(xiàn)成的,沒(méi)有引入新方法。具體的 Set 實(shí)現(xiàn)類依賴添加的對(duì)象的 equals() 方法來(lái)檢查等同性。 

          HashSet 類和 TreeSet 類 

          “集合框架”支持 Set 接口兩種普通的實(shí)現(xiàn):HashSet 和 TreeSet。在更多情況下,您會(huì)使用 HashSet 存儲(chǔ)重復(fù)自由的集合。考慮到效率,添加到 HashSet 的對(duì)象需要采用恰當(dāng)分配散列碼的方式來(lái)實(shí)現(xiàn) hashCode() 方法。雖然大多數(shù)系統(tǒng)類覆蓋了 Object 中缺省的 hashCode() 實(shí)現(xiàn),但創(chuàng)建您自己的要添加到 HashSet 的類時(shí),別忘了覆蓋 hashCode()。當(dāng)您要從集合中以有序的方式抽取元素時(shí),TreeSet 實(shí)現(xiàn)會(huì)有用處。為了能順利進(jìn)行,添加到 TreeSet 的元素必須是可排序的。 “集合框架”添加對(duì) Comparable元素的支持,在排序的“可比較的接口”部分中會(huì)詳細(xì)介紹。我們暫且假定一棵樹(shù)知道如何保持 java.lang 包裝程序器類元素的有序狀態(tài)。一般說(shuō)來(lái),先把元素添加到 HashSet,再把集合轉(zhuǎn)換為 TreeSet 來(lái)進(jìn)行有序遍歷會(huì)更快。 

          為優(yōu)化 HashSet 空間的使用,您可以調(diào)優(yōu)初始容量和負(fù)載因子。TreeSet 不包含調(diào)優(yōu)選項(xiàng),因?yàn)闃?shù)總是平衡的,保證了插入、刪除、查詢的性能為 log(n)。 

          HashSet 和 TreeSet 都實(shí)現(xiàn) Cloneable 接口。 

          集的使用示例 

          為演示具體 Set 類的使用,下面的程序創(chuàng)建了一個(gè) HashSet,并往里添加了一組名字,其中有個(gè)名字添加了兩次。接著,程序把集中名字的列表打印出來(lái),演示了重復(fù)的名字沒(méi)有出現(xiàn)。 接著,程序把集作為 TreeSet 來(lái)處理,并顯示有序的列表。 
          import java.util.*; 

          public class SetExample { 
          public static void main(String args[]) { 
          Set set = new HashSet(); 
          set.add("Bernadine"); 
          set.add("Elizabeth"); 
          set.add("Gene"); 
          set.add("Elizabeth"); 
          set.add("Clara"); 
          System.out.println(set); 
          Set sortedSet = new TreeSet(set); 
          System.out.println(sortedSet); 


          運(yùn)行程序產(chǎn)生了以下輸出。請(qǐng)注意重復(fù)的條目只出現(xiàn)了一次,列表的第二次輸出已按字母順序排序。 
          [Gene, Clara, Bernadine, Elizabeth] 
          [Bernadine, Clara, Elizabeth, Gene] 

          AbstractSet 類 

          AbstractSet 類覆蓋了 equals() 和 hashCode() 方法,以確保兩個(gè)相等的集返回相同的散列碼。若兩個(gè)集大小相等且包含相同元素,則這兩個(gè)集相等。 按定義,集散列碼是集中元素散列碼的總和。 因此,不論集的內(nèi)部順序如何,兩個(gè)相等的集會(huì)報(bào)告相同的散列碼。 


          -------------------------------------------------------------------------------- 


          練系 
          •練習(xí) 1. 如何將 HashSet 用于一個(gè)稀疏的位集 
          •練習(xí) 2. 如何使用 TreeSet 提供有序的 JList 


          -------------------------------------------------------------------------------- 


          List 接口 

          List 接口繼承了 Collection 接口以定義一個(gè)允許重復(fù)項(xiàng)的有序集合。 該接口不但能夠?qū)α斜淼囊徊糠诌M(jìn)行處理,還添加了面向位置的操作。 

          面向位置的操作包括插入某個(gè)元素或 Collection 的功能,還包括獲取、除去或更改元素的功能。 在 List 中搜索元素可以從列表的頭部或尾部開(kāi)始,如果找到元素,還將報(bào)告元素所在的位置。 
          •void add(int index, Object element) 
          •boolean addAll(int index, Collection collection) 
          •Object get(int index) 
          •int indexOf(Object element) 
          •int lastIndexOf(Object element) 
          •Object remove(int index) 
          •Object set(int index, Object element) 

          List 接口不但以位置友好的方式遍歷整個(gè)列表,還能處理集合的子集: 
          •ListIterator listIterator() 
          •ListIterator listIterator(int startIndex) 
          •List subList(int fromIndex, int toIndex) 

          處理 subList() 時(shí),位于 fromIndex 的元素在子列表中,而位于 toIndex 的元素則不是,提醒這一點(diǎn)很重要。以下 for-loop 測(cè)試案例大致反映了這一點(diǎn): 
          for (int i=fromIndex; i<toIndex; i++) { >
          // process element at position i 

          此外,我們還應(yīng)該提醒的是 ― 對(duì)子列表的更改(如 add()、remove() 和 set() 調(diào)用)對(duì)底層 List 也有影響。 

          ListIterator 接口 

          ListIterator 接口繼承 Iterator 接口以支持添加或更改底層集合中的元素,還支持雙向訪問(wèn)。 

          以下源代碼演示了列表中的反向循環(huán)。請(qǐng)注意 ListIterator 最初位于列表尾之后(list.size()),因?yàn)榈谝粋€(gè)元素的下標(biāo)是 0。 
          List list = ...; 
          ListIterator iterator = list.listIterator(list.size()); 
          while (iterator.hasPrevious()) { 
          Object element = iterator.previous(); 
          // Process element 

          正常情況下,不用 ListIterator 改變某次遍歷集合元素的方向 ― 向前或者向后。 雖然在技術(shù)上可能實(shí)現(xiàn)時(shí),但在 previous() 后立刻調(diào)用 next(),返回的是同一個(gè)元素。把調(diào)用 next() 和 previous() 的順序顛倒一下,結(jié)果相同。 

          我們還需要稍微再解釋一下 add() 操作。添加一個(gè)元素會(huì)導(dǎo)致新元素立刻被添加到隱式光標(biāo)的前面。因此,添加元素后調(diào)用 previous() 會(huì)返回新元素,而調(diào)用 next() 則不起作用,返回添加操作之前的下一個(gè)元素。 

          ArrayList 類和 LinkedList 類 

          在“集合框架”中有兩種常規(guī)的 List 實(shí)現(xiàn):ArrayList 和 LinkedList。 使用兩種 List 實(shí)現(xiàn)的哪一種取決于您特定的需要。如果要支持隨機(jī)訪問(wèn),而不必在除尾部的任何位置插入或除去元素, 那么,ArrayList 提供了可選的集合。 但如果,您要頻繁的從列表的中間位置添加和除去元素,而只要順序的訪問(wèn)列表元素,那么,LinkedList 實(shí)現(xiàn)更好。 

          ArrayList 和 LinkedList 都實(shí)現(xiàn) Cloneable 接口。此外,LinkedList 添加了一些處理列表兩端元素的方法(下圖只顯示了新方法): 

          使用這些新方法,您就可以輕松的把 LinkedList 當(dāng)作一個(gè)堆棧、隊(duì)列或其它面向端點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。 
          LinkedList queue = ...; 
          queue.addFirst(element); 
          Object object = queue.removeLast();LinkedList stack = ...; 
          stack.addFirst(element); 
          Object object = stack.removeFirst(); 
          Vector 類和 Stack 類是 List接口的歷史實(shí)現(xiàn)。我們將在Vector 類和 Stack 類中討論它們。 

          List 的使用示例 

          下面的程序演示了具體 List 類的使用。第一部分,創(chuàng)建一個(gè)由 ArrayList 支持的 List。填充完列表以后,特定條目就得到了。示例的 LinkedList 部分把 LinkedList 當(dāng)作一個(gè)隊(duì)列,從隊(duì)列頭部添加?xùn)|西,從尾部除去。 
          import java.util.*; 

          public class ListExample { 
          public static void main(String args[]) { 
          List list = new ArrayList(); 
          list.add("Bernadine"); 
          list.add("Elizabeth"); 
          list.add("Gene"); 
          list.add("Elizabeth"); 
          list.add("Clara"); 
          System.out.println(list); 
          System.out.println("2: " + list.get(2)); 
          System.out.println("0: " + list.get(0)); 
          LinkedList queue = new LinkedList(); 
          queue.addFirst("Bernadine"); 
          queue.addFirst("Elizabeth"); 
          queue.addFirst("Gene"); 
          queue.addFirst("Elizabeth"); 
          queue.addFirst("Clara"); 
          System.out.println(queue); 
          queue.removeLast(); 
          queue.removeLast(); 
          System.out.println(queue); 


          運(yùn)行程序產(chǎn)生了以下輸出。請(qǐng)注意,與 Set 不同的是 List 允許重復(fù)。 
          [Bernadine, Elizabeth, Gene, Elizabeth, Clara] 
          2: Gene 
          0: Bernadine 
          [Clara, Elizabeth, Gene, Elizabeth, Bernadine] 
          [Clara, Elizabeth, Gene] 
          AbstractList 類和 AbstractSequentialList 類 

          有兩個(gè)抽象的 List 實(shí)現(xiàn)類:AbstractList 和 AbstractSequentialList。像 AbstractSet 類一樣,它們覆蓋了 equals() 和 hashCode() 方法以確保兩個(gè)相等的集合返回相同的散列碼。若兩個(gè)集大小相等且包含順序相同的相同元素,則這兩個(gè)集相等。 這里的 hashCode() 實(shí)現(xiàn)在 List 接口定義中指定,而在這里實(shí)現(xiàn)。 

          除了 equals() 和 hashCode() 實(shí)現(xiàn),AbstractList 和 AbstractSequentialList 實(shí)現(xiàn)了其余 List 方法的一部分。因?yàn)閿?shù)據(jù)源隨機(jī)訪問(wèn)和順序訪問(wèn)是分別實(shí)現(xiàn)的,使得具體列表實(shí)現(xiàn)的創(chuàng)建更為容易。需要定義的一套方法取決于您希望支持的行為。下表應(yīng)該能夠幫您記住需要實(shí)現(xiàn)哪些方法。 您永遠(yuǎn)不必親自提供的是 Iterator iterator() 方法的實(shí)現(xiàn)。 

          AbstractList AbstractSequentialList 


          不可修改 Object get(int index) ListIterator listIterator(int index) 
          int size() - boolean hasNext() 
          - Object next() 
          - int nextIndex() 
          - boolean hasPrevious() 
          - Object previous() 
          - int previousIndex() 
          int size() 


          可修改 Object get(int index) ListIterator listIterator(int index) 
          int size() - boolean hasNext() 
          Object set(int index, Object element) - Object next() 
          - int nextIndex() 
          - boolean hasPrevious() 
          - Object previous() 
          - int previousIndex() 
          int size() 
          ListIterator 
          - set(Object element) 

          變量大小和可修改性 Object get(int index) ListIterator listIterator(int index) 
          int size() - boolean hasNext() 
          Object set(int index, Object element) - Object next() 
          add(int index, Object element) - int nextIndex() 
          Object remove(int index) - boolean hasPrevious() 
          - Object previous() 
          - int previousIndex() 
          int size() 
          ListIterator 
          - set(Object element) 
          ListIterator 
          - add(Object element) 
          - remove() 

          正如 Collection 接口文檔所述,您還應(yīng)該提供兩個(gè)構(gòu)造函數(shù),一個(gè)無(wú)參數(shù),一個(gè)接受另一個(gè) Collection。 

          -------------------------------------------------------------------------------- 

          練習(xí) 
          •練習(xí) 3. 如何用 JComboBox 實(shí)現(xiàn) ArrayList 

          -------------------------------------------------------------------------------- 

          Map 接口 

          Map 接口不是 Collection 接口的繼承。而是從自己的用于維護(hù)鍵-值關(guān)聯(lián)的接口層次結(jié)構(gòu)入手。按定義,該接口描述了從不重復(fù)的鍵到值的映射。 

          我們可以把這個(gè)接口方法分成三組操作:改變、查詢和提供可選視圖。 

          改變操作允許您從映射中添加和除去鍵-值對(duì)。鍵和值都可以為 null。但是,您不能把 Map 作為一個(gè)鍵或值添加給自身。 
          •Object put(Object key, Object value) 
          •Object remove(Object key) 
          •void putAll(Map mapping) 
          •void clear() 

          查詢操作允許您檢查映射內(nèi)容: 
          •Object get(Object key) 
          •boolean containsKey(Object key) 
          •boolean containsValue(Object value) 
          •int size() 
          •boolean isEmpty() 

          最后一組方法允許您把鍵或值的組作為集合來(lái)處理。 
          •public Set keySet() 
          •public Collection values() 
          •public Set entrySet() 

          因?yàn)橛成渲墟I的集合必須是唯一的,您用 Set 支持。因?yàn)橛成渲兄档募峡赡懿晃ㄒ唬?Collection 支持。最后一個(gè)方法返回一個(gè)實(shí)現(xiàn) Map.Entry 接口的元素 Set。 

          Map.Entry 接口 

          Map 的 entrySet() 方法返回一個(gè)實(shí)現(xiàn) Map.Entry 接口的對(duì)象集合。集合中每個(gè)對(duì)象都是底層 Map 中一個(gè)特定的鍵-值對(duì)。 

          通過(guò)這個(gè)集合迭代,您可以獲得每一條目的鍵或值并對(duì)值進(jìn)行更改。但是,如果底層 Map 在 Map.Entry 接口的 setValue() 方法外部被修改,此條目集就會(huì)變得無(wú)效,并導(dǎo)致迭代器行為未定義。 

          HashMap 類和 TreeMap 類 

          “集合框架”提供兩種常規(guī)的 Map 實(shí)現(xiàn):HashMap 和 TreeMap。和所有的具體實(shí)現(xiàn)一樣,使用哪種實(shí)現(xiàn)取決于您的特定需要。 在 Map 中插入、刪除和定位元素,HashMap 是最好的選擇。 但如果您要按順序遍歷鍵,那么 TreeMap 會(huì)更好。根據(jù)集合大小,先把元素添加到 HashMap,再把這種映射轉(zhuǎn)換成一個(gè)用于有序鍵遍歷的 TreeMap 可能更快。使用 HashMap 要求添加的鍵類明確定義了 hashCode() 實(shí)現(xiàn)。有了 TreeMap實(shí)現(xiàn),添加到映射的元素一定是可排序的。我們將在排序中詳細(xì)介紹。 

          為了優(yōu)化 HashMap 空間的使用,您可以調(diào)優(yōu)初始容量和負(fù)載因子。這個(gè) TreeMap 沒(méi)有調(diào)優(yōu)選項(xiàng),因?yàn)樵摌?shù)總處于平衡狀態(tài)。 

          HashMap 和 TreeMap 都實(shí)現(xiàn) Cloneable 接口。 

          Hashtable 類和 Properties 類是 Map接口的歷史實(shí)現(xiàn)。我們將在Dictionary 類、Hashtable 類和 Properties 類中討論。 

          映射的使用示例 

          以下程序演示了具體 Map 類的使用。該程序?qū)ψ悦钚袀鬟f的詞進(jìn)行頻率計(jì)數(shù)。HashMap 起初用于數(shù)據(jù)存儲(chǔ)。后來(lái),映射被轉(zhuǎn)換為 TreeMap 以顯示有序的鍵列列表。 
          import java.util.*; 

          public class MapExample { 
          public static void main(String args[]) { 
          Map map = new HashMap(); 
          Integer ONE = new Integer(1); 
          for (int i=0, n=args.length; i<n; i++) { >
          String key = args[i]; 
          Integer frequency = (Integer)map.get(key); 
          if (frequency == null) { 
          frequency = ONE; 
          } else { 
          int value = frequency.intValue(); 
          frequency = new Integer(value + 1); 

          map.put(key, frequency); 

          System.out.println(map); 
          Map sortedMap = new TreeMap(map); 
          System.out.println(sortedMap); 


          用 Bill of Rights 的第三篇文章的文本運(yùn)行程序產(chǎn)生下列輸出,請(qǐng)注意有序輸出看起來(lái)多么有用! 

          無(wú)序輸出: 

          {prescribed=1, a=1, time=2, any=1, no=1, shall=1, nor=1, peace=1, owner=1, soldier=1, to=1, the=2, law=1, but=1, manner=1, without=1, house=1, in=4, by=1, consent=1, war=1, quartered=1, be=2, of=3} 

          有序輸出: 

          {a=1, any=1, be=2, but=1, by=1, consent=1, house=1, in=4, law=1, manner=1, no=1, nor=1, of=3, owner=1, peace=1, prescribed=1, quartered=1, shall=1, soldier=1, the=2, time=2, to=1, war=1, without=1} 

          AbstractMap 類 

          和其它抽象集合實(shí)現(xiàn)相似,AbstractMap 類覆蓋了 equals() 和 hashCode() 方法以確保兩個(gè)相等映射返回相同的散列碼。如果兩個(gè)映射大小相等、包含同樣的鍵且每個(gè)鍵在這兩個(gè)映射中對(duì)應(yīng)的值都相同,則這兩個(gè)映射相等。按定義,映射的散列碼是映射元素散列碼的總和, 其中每個(gè)元素是 Map.Entry 接口的一個(gè)實(shí)現(xiàn)。因此,不論映射內(nèi)部順序如何,兩個(gè)相等映射會(huì)報(bào)告相同的散列碼。 

          WeakHashMap 類 

          WeakHashMap 是 Map 的一個(gè)特殊實(shí)現(xiàn),它只用于存儲(chǔ)對(duì)鍵的弱引用。 當(dāng)映射的某個(gè)鍵在 WeakHashMap 的外部不再被引用時(shí),就允許垃圾收集器收集映射中相應(yīng)的鍵值對(duì)。 使用 WeakHashMap 有益于保持類似注冊(cè)表的數(shù)據(jù)結(jié)構(gòu),其中條目的鍵不再能被任何線程訪問(wèn)時(shí),此條目就沒(méi)用了。 

          Java 2 SDK,標(biāo)準(zhǔn)版,版本 1.3 添加了一個(gè)構(gòu)造函數(shù)到 WeakHashMap,它接受 Map。在 Java 2 平臺(tái)版本 1.2 中,該構(gòu)造函數(shù)只允許覆蓋缺省負(fù)載因子和初始容量設(shè)置,不允許在另一個(gè)映射的基礎(chǔ)上初始化映射(如 Map 接口文檔中所介紹)。 


          -------------------------------------------------------------------------------- 

          排序 

          為了用“集合框架”的額外部分把排序支持添加到 Java 2 SDK,版本 1.2,核心 Java 庫(kù)作了許多更改。 像 String 和 Integer 類如今實(shí)現(xiàn) Comparable 接口以提供自然排序順序。 對(duì)于那些沒(méi)有自然順序的類、或者當(dāng)您想要一個(gè)不同于自然順序的順序時(shí),您可以實(shí)現(xiàn) Comparator 接口來(lái)定義您自己的。 

          為了利用排序功能,“集合框架”提供了兩種使用該功能的接口:SortedSet 和 SortedMap。 

          Comparable 接口 

          在 java.lang 包中,Comparable 接口適用于一個(gè)類有自然順序的時(shí)候。假定對(duì)象集合是同一類型,該接口允許您把集合排序成自然順序。 

          compareTo() 方法比較當(dāng)前實(shí)例和作為參數(shù)傳入的元素。如果排序過(guò)程中當(dāng)前實(shí)例出現(xiàn)在參數(shù)前,就返回某個(gè)負(fù)值。如果當(dāng)前實(shí)例出現(xiàn)在參數(shù)后,則返回正值。否則,返回零。這里不要求零返回值表示元素相等。零返回值只是表示兩個(gè)對(duì)象排在同一個(gè)位置。 

          在 Java 2 SDK,版本 1.2 中有十四個(gè)類實(shí)現(xiàn) Comparable 接口。下表展示了它們的自然排序。 雖然一些類共享同一種自然排序,但只有相互可比的類才能排序。 

          類 排序 

          BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short 按數(shù)字大小排序 

          Character 按 Unicode 值的數(shù)字大小排序 

          CollationKey 按語(yǔ)言環(huán)境敏感的字符串排序 

          Date 按年代排序 

          File 按系統(tǒng)特定的路徑名的全限定字符的 Unicode 值排序 

          ObjectStreamField 按名字中字符的 Unicode 值排序 

          String 按字符串中字符 Unicode 值排序 


          String 的 compareTo() 方法的文檔按詞典的形式定義了排序。這意味著比較只是在文本中被數(shù)字化了的字符串值之間,其中文本沒(méi)有必要按所有語(yǔ)言的字母順序排序。 對(duì)于語(yǔ)言環(huán)境特定的排序,通過(guò) CollationKey 使用 Collator。 

          下面演示了通過(guò) CollationKey 使用 Collator 進(jìn)行語(yǔ)言環(huán)境特定的排序: 
          import java.text.*; 
          import java.util.*; 

          public class CollatorTest { 
          public static void main(String args[]) { 
          Collator collator = Collator.getInstance(); 
          CollationKey key1 = collator.getCollationKey("Tom"); 
          CollationKey key2 = collator.getCollationKey("tom"); 
          CollationKey key3 = collator.getCollationKey("thom"); 
          CollationKey key4 = collator.getCollationKey("Thom"); 
          CollationKey key5 = collator.getCollationKey("Thomas"); 

          Set set = new TreeSet(); 
          set.add(key1); 
          set.add(key2); 
          set.add(key3); 
          set.add(key4); 
          set.add(key5); 
          printCollection(set); 

          static private void printCollection( 
          Collection collection) { 
          boolean first = true; 
          Iterator iterator = collection.iterator(); 
          System.out.print("["); 
          while (iterator.hasNext()) { 
          if (first) { 
          first = false; 
          } else { 
          System.out.print(", "); 

          CollationKey key = (CollationKey)iterator.next(); 
          System.out.print(key.getSourceString()); 

          System.out.println("]"); 


          運(yùn)行程序產(chǎn)生了以下輸出。 
          [thom, Thom, Thomas, tom, Tom] 
          如果沒(méi)有用 Collator,而是直接的存儲(chǔ)名字,那么小寫(xiě)的名字會(huì)和大寫(xiě)的名字分開(kāi)顯示: 
          [Thom, Thomas, Tom, thom, tom] 
          創(chuàng)建您自己的類 Comparable 只是個(gè)實(shí)現(xiàn) compareTo() 方法的問(wèn)題。通常就是依賴幾個(gè)數(shù)據(jù)成員的自然排序。您自己的類也應(yīng)該覆蓋 equals() 和 hashCode() 以確保兩個(gè)相等的對(duì)象返回同一個(gè)散列碼。 

          Comparator 接口 

          若一個(gè)類不能用于實(shí)現(xiàn) java.lang.Comparable,您可以提供自己的 java.util.Comparator 行為。如果您不喜歡缺省的 Comparable 行為,您照樣可以提供自己的 Comparator。 

          Comparator 的 compare() 方法的返回值和 Comparable 的 compareTo() 方法的返回值相似。在此情況下,如果排序時(shí)第一個(gè)元素出現(xiàn)在第二個(gè)元素之前,則返回一個(gè)負(fù)值。如果第一個(gè)元素出現(xiàn)在后,那么返回一個(gè)正值。 否則,返回零。與 Comparable 相似,零返回值不表示元素相等。一個(gè)零返回值只是表示兩個(gè)對(duì)象排在同一位置。由 Comparator 用戶決定如何處理。 如果兩個(gè)不相等的元素比較的結(jié)果為零,您首先應(yīng)該確信那就是您要的結(jié)果,然后記錄行為。 

          為了演示,您會(huì)發(fā)現(xiàn)編寫(xiě)一個(gè)新的忽略大小寫(xiě)的 Comparator,代替使用 Collator 進(jìn)行語(yǔ)言環(huán)境特定、忽略大小寫(xiě)的比較會(huì)更容易。這樣的一種實(shí)現(xiàn)如下所示: 
          class CaseInsensitiveComparator implements Comparator { 
          public int compare(Object element1, Object element2) { 
          String lowerE1 = ((String)element1).toLowerCase(); 
          String lowerE2 = ((String)element2).toLowerCase(); 
          return lowerE1.compareTo(lowerE2); 


          因?yàn)槊總€(gè)類在某些地方都建立了 Object 子類,所以這不是您實(shí)現(xiàn) equals() 方法的必要條件。實(shí)際上大多數(shù)情況下您不會(huì)去這樣做。切記該 equals() 方法檢查的是 Comparator 實(shí)現(xiàn)的等同性,不是處于比較狀態(tài)下的對(duì)象。 

          Collections 類有個(gè)預(yù)定義的 Comparator 用于重用。 調(diào)用 Collections.reverseOrder() 返回一個(gè) Comparator,它對(duì)逆序?qū)崿F(xiàn) Comparable 接口的對(duì)象進(jìn)行排序。 

          練習(xí) 
          •練習(xí) 4. 如何使用映射對(duì)詞計(jì)數(shù) 

          SortedSet 接口 

          “集合框架”提供了個(gè)特殊的 Set 接口:SortedSet,它保持元素的有序順序。 

          該接口為集的子集和它的兩端(即頭和尾)提供了訪問(wèn)方法。當(dāng)您處理列表的子集時(shí),更改子集會(huì)反映到源集。 此外,更改源集也會(huì)反映在子集上。發(fā)生這種情況的原因在于子集由兩端的元素而不是下標(biāo)元素指定。 此外,如果 fromElement 是源集的一部分,它就是子集的一部分。但如果 toElement 是源集的一部分,它卻不是子集的一部分。如果您想要一個(gè)特殊的高端元素(to-element)在子集中,您必須找到下一個(gè)元素。對(duì)于一個(gè) String 來(lái)說(shuō),下一個(gè)元素是個(gè)附帶空字符的同一個(gè)字符串(string+"\0")。; 

          添加到 SortedSet 的元素必須實(shí)現(xiàn) Comparable,否則您必須給它的實(shí)現(xiàn)類的構(gòu)造函數(shù)提供一個(gè) Comparator:TreeSet(您可以自己實(shí)現(xiàn)接口。但是“集合框架”只提供這樣一個(gè)具體的實(shí)現(xiàn)類。) 

          為了演示,以下示例使用 Collections 類中逆序的 Comparator。 
          Comparator comparator = Collections.reverseOrder(); 
          Set reverseSet = new TreeSet(comparator); 
          reverseSet.add("Bernadine"); 
          reverseSet.add("Elizabeth"); 
          reverseSet.add("Gene"); 
          reverseSet.add("Elizabeth"); 
          reverseSet.add("Clara"); 
          System.out.println(reverseSet); 
          運(yùn)行程序產(chǎn)生了以下輸出。 
          [Gene, Elizabeth, Clara, Bernadine] 
          因?yàn)榧仨毎ㄒ坏捻?xiàng),如果添加元素時(shí)比較兩個(gè)元素導(dǎo)致了零返回值(通過(guò) Comparable 的 compareTo() 方法或 Comparator 的 compare() 方法)那么新元素就沒(méi)有添加進(jìn)去。如果兩個(gè)元素相等,那還好。但如果它們不相等的話,您接下來(lái)就應(yīng)該修改比較方法,讓比較方法和 equals() 方法一致。 

          使用先前的 CaseInsensitiveComparator 演示這一問(wèn)題,產(chǎn)生了一個(gè)三元素集:thom、Thomas 和 Tom,而不是可能預(yù)期的五個(gè)元素。 
          Comparator comparator = new CaseInsensitiveComparator(); 
          Set set = new TreeSet(comparator); 
          set.add("Tom"); 
          set.add("tom"); 
          set.add("thom"); 
          set.add("Thom"); 
          set.add("Thomas"); 

          SortedMap 接口 

          “集合框架”提供了個(gè)特殊的Map 接口:SortedMap,它用來(lái)保持鍵的有序順序。 

          此接口為映射的子集包括兩個(gè)端點(diǎn)提供了訪問(wèn)方法。除了排序是作用于映射的鍵以外,處理 SortedMap 和處理 SortedSet 一樣。“集合框架”提供的實(shí)現(xiàn)類是 TreeMap。 

          因?yàn)閷?duì)于映射來(lái)說(shuō),每個(gè)鍵只能對(duì)應(yīng)一個(gè)值,如果在添加一個(gè)鍵-值對(duì)時(shí)比較兩個(gè)鍵產(chǎn)生了零返回值(通過(guò) Comparable 的 compareTo() 方法或通過(guò) Comparator 的 compare() 方法),那么,原始鍵對(duì)應(yīng)值被新的值替代。如果兩個(gè)元素相等,那還好。 但如果不相等,那么您就應(yīng)該修改比較方法,讓比較方法和 equals() 的效果一致。 

          posted on 2012-09-20 00:42 奮斗成就男人 閱讀(417) 評(píng)論(0)  編輯  收藏 所屬分類: java

          主站蜘蛛池模板: 斗六市| 仙桃市| 焉耆| 普兰店市| 自治县| 长宁县| 阿城市| 镇赉县| 怀集县| 华池县| 瑞昌市| 宾阳县| 高台县| 高清| 海安县| 汤阴县| 荣昌县| 德昌县| 贵定县| 山丹县| 天全县| 广安市| 罗定市| 云梦县| 榆树市| 亳州市| 长泰县| 新乐市| 长寿区| 蕉岭县| 昆山市| 扶风县| 屏南县| 安多县| 德庆县| 澎湖县| 阿拉善左旗| 尤溪县| 全州县| 永善县| 工布江达县|