您好,登錄后才能下訂單哦!
Java提供了容納對象(或者對象的句柄)的多種方式,接下來我們具體看看都有哪些方式。
有兩方面的問題將數組與其他集合類型區分開來:效率和類型。對于Java來說,為保存和訪問一系列對象(實際是對象的句柄)數組,最有效的方法莫過于數組。數組實際代表一個簡單的線性序列,它使得元素的訪問速度非常快,但我們卻要為這種速度付出代價:創建一個數組對象時,它的大小是固定的,而且不可在那個數組對象的“存在時間”內發生改變。可創建特定大小的一個數組,然后假如用光了存儲空間,就再創建一個新數組,將所有句柄從舊數組移到新數組。這屬于“矢量”(Vector)類的行為。然而,由于為這種大小的靈活性要付出較大的代價,所以我們認為矢量的效率并沒有數組高。
在Java中,無論使用的是數組還是集合,都會進行范圍檢查——若超過邊界,就會獲得一個RuntimeException(運行期違例)錯誤。幾種常見的集合類:Vector(矢量)、Stack(堆棧)以及Hashtable(散列表)。這些類都涉及對對象的處理——好象它們沒有特定的類型。換言之,它們將其當作Object類型處理(Object類型是Java中所有類的“根”類)。從某個角度看,這種處理方法是非常合理的:我們僅需構建一個集合,然后任何Java對象都可以進入那個集合(除基本數據類型外——可用Java的基本類型封裝類將其作為常數置入集合,或者將其封裝到自己的類內,作為可以變化的值使用)。這再一次反映了數組優于常規集合:創建一個數組時,可令其容納一種特定的類型。
對象數組容納的是句柄,而基本數據類型數組容納的是具體的數值。
1、集合
為容納一組對象,最適宜的選擇應當是數組。而且假如容納的是一系列基本數據類型,更是必須采用數組。當我們編寫程序時,通常并不能確切地知道最終需要多少個對象。有些時候甚至想用更復雜的方式來保存對象。為解決這個問題,Java提供了四種類型的“集合類”:Vector(矢量)、BitSet(位集)、Stack(堆棧)以及Hashtable(散列表)。與擁有集合功能的其他語言相比,盡管這兒的數量顯得相當少,但仍然能用它們解決數量驚人的實際問題。
這些集合類具有形形色色的特征。例如,Stack實現了一個LIFO(先入先出)序列,而Hashtable是一種“關聯數組”,允許我們將任何對象關聯起來。除此以外,所有Java集合類都能自動改變自身的大小。所以,我們在編程時可使用數量眾多的對象,同時不必擔心會將集合弄得有多大。
1.1 缺點:類型未知
使用Java集合的“缺點”是在將對象置入一個集合時丟失了類型信息。之所以會發生這種情況,是由于當初編寫集合時,那個集合的程序員根本不知道用戶到底想把什么類型置入集合。若指示某個集合只允許特定的類型,會妨礙它成為一個“常規用途”的工具,為用戶帶來麻煩。為解決這個問題,集合實際容納的是類型為Object的一些對象的句柄。這種類型當然代表Java中的所有對象,因為它是所有類的根。當然,也要注意這并不包括基本數據類型,因為它們并不是從“任何東西”繼承來的。這是一個很好的方案,只是不適用下述場合:
(1) 將一個對象句柄置入集合時,由于類型信息會被拋棄,所以任何類型的對象都可進入我們的集合——即便特別指示它只能容納特定類型的對象。舉個例子來說,雖然指示它只能容納貓,但事實上任何人都可以把一條狗扔進來。
(2) 由于類型信息不復存在,所以集合能肯定的唯一事情就是自己容納的是指向一個對象的句柄。正式使用它之前,必須對其進行造型,使其具有正確的類型。
2、Stack
Stack有時也可以稱為“后入先出”(LIFO)集合。換言之,我們在堆棧里最后“壓入”的東西將是以后第一個“彈出”的。和其他所有Java集合一樣,我們壓入和彈出的都是“對象”,所以必須對自己彈出的東西進行“造型”。
一種很少見的做法是拒絕使用Vector作為一個Stack的基本構成元素,而是從Vector里“繼承”一個Stack。這樣一來,它就擁有了一個Vector的所有特征及行為,另外加上一些額外的Stack行為。很難判斷出設計者到底是明確想這樣做,還是屬于一種固有的設計。
下面是一個簡單的堆棧示例,它能讀入數組的每一行,同時將其作為字串壓入堆棧。
import java.util.*; public class Stacks { static String[] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; public static void main(String[] args) { Stack stk = new Stack(); for(int i = 0; i < months.length; i++) stk.push(months[i] + " "); System.out.println("stk = " + stk); // Treating a stack as a Vector: stk.addElement("The last line"); System.out.println( "element 5 = " + stk.elementAt(5)); System.out.println("popping elements:"); while(!stk.empty()) System.out.println(stk.pop()); } }
months數組的每一行都通過push()繼承進入堆棧,稍后用pop()從堆棧的頂部將其取出。要聲明的一點是,Vector操作亦可針對Stack對象進行。這可能是由繼承的特質決定的——Stack“屬于”一種Vector。因此,能對Vector進行的操作亦可針對Stack進行,例如elementAt()方法。
3、Hashtable
Vector允許我們用一個數字從一系列對象中作出選擇,所以它實際是將數字同對象關聯起來了。但假如我們想根據其他標準選擇一系列對象呢?堆棧就是這樣的一個例子:它的選擇標準是“最后壓入堆棧的東西”。這種“從一系列對象中選擇”的概念亦可叫作一個“映射”、“字典”或者“關聯數組”。從概念上講,它看起來象一個Vector,但卻不是通過數字來查找對象,而是用另一個對象來查找它們!這通常都屬于一個程序中的重要進程。
在Java中,這個概念具體反映到抽象類Dictionary身上。該類的接口是非常直觀的size()告訴我們其中包含了多少元素;isEmpty()判斷是否包含了元素(是則為true);put(Object key, Object value)添加一個值(我們希望的東西),并將其同一個鍵關聯起來(想用于搜索它的東西);get(Object key)獲得與某個鍵對應的值;而remove(Object Key)用于從列表中刪除“鍵-值”對。還可以使用枚舉技術:keys()產生對鍵的一個枚舉(Enumeration);而elements()產生對所有值的一個枚舉。這便是一個Dictionary(字典)的全部。
Dictionary的實現過程并不麻煩。下面列出一種簡單的方法,它使用了兩個Vector,一個用于容納鍵,另一個用來容納值:
import java.util.*; public class AssocArray extends Dictionary { private Vector keys = new Vector(); private Vector values = new Vector(); public int size() { return keys.size(); } public boolean isEmpty() { return keys.isEmpty(); } public Object put(Object key, Object value) { keys.addElement(key); values.addElement(value); return key; } public Object get(Object key) { int index = keys.indexOf(key); // indexOf() Returns -1 if key not found: if(index == -1) return null; return values.elementAt(index); } public Object remove(Object key) { int index = keys.indexOf(key); if(index == -1) return null; keys.removeElementAt(index); Object returnval = values.elementAt(index); values.removeElementAt(index); return returnval; } public Enumeration keys() { return keys.elements(); } public Enumeration elements() { return values.elements(); } // Test it: public static void main(String[] args) { AssocArray aa = new AssocArray(); for(char c = 'a'; c <= 'z'; c++) aa.put(String.valueOf(c), String.valueOf(c) .toUpperCase()); char[] ca = { 'a', 'e', 'i', 'o', 'u' }; for(int i = 0; i < ca.length; i++) System.out.println("Uppercase: " + aa.get(String.valueOf(ca[i]))); } }
在對AssocArray的定義中,我們注意到的第一個問題是它“擴展”了字典。這意味著AssocArray屬于Dictionary的一種類型,所以可對其發出與Dictionary一樣的請求。如果想生成自己的Dictionary,而且就在這里進行,那么要做的全部事情只是填充位于Dictionary內的所有方法(而且必須覆蓋所有方法,因為它們——除構建器外——都是抽象的)。
Vector key和value通過一個標準索引編號鏈接起來。也就是說,如果用“roof”的一個鍵以及“blue”的一個值調用put()——假定我們準備將一個房子的各部分與它們的油漆顏色關聯起來,而且AssocArray里已有100個元素,那么“roof”就會有101個鍵元素,而“blue”有101個值元素。而且要注意一下get(),假如我們作為鍵傳遞“roof”,它就會產生與keys.index.Of()的索引編號,然后用那個索引編號生成相關的值矢量內的值。
main()中進行的測試是非常簡單的;它只是將小寫字符轉換成大寫字符,這顯然可用更有效的方式進行。但它向我們揭示出了AssocArray的強大功能。
標準Java庫只包含Dictionary的一個變種,名為Hashtable(散列表,注釋③)。Java的散列表具有與AssocArray相同的接口(因為兩者都是從Dictionary繼承來的)。但有一個方面卻反映出了差別:執行效率。若仔細想想必須為一個get()做的事情,就會發現在一個Vector里搜索鍵的速度要慢得多。但此時用散列表卻可以加快不少速度。不必用冗長的線性搜索技術來查找一個鍵,而是用一個特殊的值,名為“散列碼”。散列碼可以獲取對象中的信息,然后將其轉換成那個對象“相對唯一”的整數(int)。所有對象都有一個散列碼,而hashCode()是根類Object的一個方法。Hashtable獲取對象的hashCode(),然后用它快速查找鍵。這樣可使性能得到大幅度提升(④)。散列表的具體工作原理已超出了本書的范圍(⑤)——大家只需要知道散列表是一種快速的“字典”(Dictionary)即可,而字典是一種非常有用的工具。
③:如計劃使用RMI(在第15章詳述),應注意將遠程對象置入散列表時會遇到一個問題(參閱《Core Java》,作者Conrell和Horstmann,Prentice-Hall 1997年出版)
④:如這種速度的提升仍然不能滿足你對性能的要求,甚至可以編寫自己的散列表例程,從而進一步加快表格的檢索過程。這樣做可避免在與Object之間進行造型的時間延誤,也可以避開由Java類庫散列表例程內建的同步過程。
⑤:我的知道的最佳參考讀物是《Practical Algorithms for Programmers》,作者為Andrew Binstock和John Rex,Addison-Wesley 1995年出版。
作為應用散列表的一個例子,可考慮用一個程序來檢驗Java的Math.random()方法的隨機性到底如何。在理想情況下,它應該產生一系列完美的隨機分布數字。但為了驗證這一點,我們需要生成數量眾多的隨機數字,然后計算落在不同范圍內的數字多少。散列表可以極大簡化這一工作,因為它能將對象同對象關聯起來(此時是將Math.random()生成的值同那些值出現的次數關聯起來)。如下所示:
//: Statistics.java // Simple demonstration of Hashtable import java.util.*; class Counter { int i = 1; public String toString() { return Integer.toString(i); } } class Statistics { public static void main(String[] args) { Hashtable ht = new Hashtable(); for(int i = 0; i < 10000; i++) { // Produce a number between 0 and 20: Integer r = new Integer((int)(Math.random() * 20)); if(ht.containsKey(r)) ((Counter)ht.get(r)).i++; else ht.put(r, new Counter()); } System.out.println(ht); } }
在main()中,每次產生一個隨機數字,它都會封裝到一個Integer對象里,使句柄能夠隨同散列表一起使用(不可對一個集合使用基本數據類型,只能使用對象句柄)。containKey()方法檢查這個鍵是否已經在集合里(也就是說,那個數字以前發現過嗎?)若已在集合里,則get()方法獲得那個鍵關聯的值,此時是一個Counter(計數器)對象。計數器內的值i隨后會增加1,表明這個特定的隨機數字又出現了一次。
假如鍵以前尚未發現過,那么方法put()仍然會在散列表內置入一個新的“鍵-值”對。在創建之初,Counter會自己的變量i自動初始化為1,它標志著該隨機數字的第一次出現。
為顯示散列表,只需把它簡單地打印出來即可。Hashtable toString()方法能遍歷所有鍵-值對,并為每一對都調用toString()。Integer toString()是事先定義好的,可看到計數器使用的toString。一次運行的結果(添加了一些換行)如下:
{19=526, 18=533, 17=460, 16=513, 15=521, 14=495, 13=512, 12=483, 11=488, 10=487, 9=514, 8=523, 7=497, 6=487, 5=480, 4=489, 3=509, 2=503, 1=475, 0=505}
大家或許會對Counter類是否必要感到疑惑,它看起來似乎根本沒有封裝類Integer的功能。為什么不用int或Integer呢?事實上,由于所有集合能容納的僅有對象句柄,所以根本不可以使用整數。學過集合后,封裝類的概念對大家來說就可能更容易理解了,因為不可以將任何基本數據類型置入集合里。然而,我們對Java封裝器能做的唯一事情就是將其初始化成一個特定的值,然后讀取那個值。也就是說,一旦封裝器對象已經創建,就沒有辦法改變一個值。這使得Integer封裝器對解決我們的問題毫無意義,所以不得不創建一個新類,用它來滿足自己的要求。
3.1 創建“關鍵”類
在前面的例子里,我們用一個標準庫的類(Integer)作為Hashtable的一個鍵使用。作為一個鍵,它能很好地工作,因為它已經具備正確運行的所有條件。但在使用散列表的時候,一旦我們創建自己的類作為鍵使用,就會遇到一個很常見的問題。例如,假設一套天氣預報系統將Groundhog(土拔鼠)對象匹配成Prediction(預報)。這看起來非常直觀:我們創建兩個類,然后將Groundhog作為鍵使用,而將Prediction作為值使用。如下所示:
//: SpringDetector.java // Looks plausible, but doesn't work right. import java.util.*; class Groundhog { int ghNumber; Groundhog(int n) { ghNumber = n; } } class Prediction { boolean shadow = Math.random() > 0.5; public String toString() { if(shadow) return "Six more weeks of Winter!"; else return "Early Spring!"; } } public class SpringDetector { public static void main(String[] args) { Hashtable ht = new Hashtable(); for(int i = 0; i < 10; i++) ht.put(new Groundhog(i), new Prediction()); System.out.println("ht = " + ht + "\n"); System.out.println( "Looking up prediction for groundhog #3:"); Groundhog gh = new Groundhog(3); if(ht.containsKey(gh)) System.out.println((Prediction)ht.get(gh)); } }
每個Groundhog都具有一個標識號碼,所以赤了在散列表中查找一個Prediction,只需指示它“告訴我與Groundhog號碼3相關的Prediction”。Prediction類包含了一個布爾值,用Math.random()進行初始化,以及一個toString()為我們解釋結果。在main()中,用Groundhog以及與它們相關的Prediction填充一個散列表。散列表被打印出來,以便我們看到它們確實已被填充。隨后,用標識號碼為3的一個Groundhog查找與Groundhog #3對應的預報。
看起來似乎非常簡單,但實際是不可行的。問題在于Groundhog是從通用的Object根類繼承的(若當初未指定基礎類,則所有類最終都是從Object繼承的)。事實上是用Object的hashCode()方法生成每個對象的散列碼,而且默認情況下只使用它的對象的地址。所以,Groundhog(3)的第一個實例并不會產生與Groundhog(3)第二個實例相等的散列碼,而我們用第二個實例進行檢索。
大家或許認為此時要做的全部事情就是正確地覆蓋hashCode()。但這樣做依然行不能,除非再做另一件事情:覆蓋也屬于Object一部分的equals()。當散列表試圖判斷我們的鍵是否等于表內的某個鍵時,就會用到這個方法。同樣地,默認的Object.equals()只是簡單地比較對象地址,所以一個Groundhog(3)并不等于另一個Groundhog(3)。
因此,為了在散列表中將自己的類作為鍵使用,必須同時覆蓋hashCode()和equals(),就象下面展示的那樣:
//: SpringDetector2.java // If you create a class that's used as a key in // a Hashtable, you must override hashCode() // and equals(). import java.util.*; class Groundhog2 { int ghNumber; Groundhog2(int n) { ghNumber = n; } public int hashCode() { return ghNumber; } public boolean equals(Object o) { return (o instanceof Groundhog2) && (ghNumber == ((Groundhog2)o).ghNumber); } } public class SpringDetector2 { public static void main(String[] args) { Hashtable ht = new Hashtable(); for(int i = 0; i < 10; i++) ht.put(new Groundhog2(i),new Prediction()); System.out.println("ht = " + ht + "\n"); System.out.println( "Looking up prediction for groundhog #3:"); Groundhog2 gh = new Groundhog2(3); if(ht.containsKey(gh)) System.out.println((Prediction)ht.get(gh)); } }
注意這段代碼使用了來自前一個例子的Prediction,所以SpringDetector.java必須首先編譯,否則就會在試圖編譯SpringDetector2.java時得到一個編譯期錯誤。
Groundhog2.hashCode()將土拔鼠號碼作為一個標識符返回(在這個例子中,程序員需要保證沒有兩個土拔鼠用同樣的ID號碼并存)。為了返回一個獨一無二的標識符,并不需要hashCode(),equals()方法必須能夠嚴格判斷兩個對象是否相等。
equals()方法要進行兩種檢查:檢查對象是否為null;若不為null,則繼續檢查是否為Groundhog2的一個實例(要用到instanceof關鍵字)。即使為了繼續執行equals(),它也應該是一個Groundhog2。正如大家看到的那樣,這種比較建立在實際ghNumber的基礎上。這一次一旦我們運行程序,就會看到它終于產生了正確的輸出(許多Java庫的類都覆蓋了hashcode()和equals()方法,以便與自己提供的內容適應)。
3.2 屬性:Hashtable的一種類型
在第一個例子中,我們使用了一個名為Properties(屬性)的Hashtable類型。在那個例子中,下述程序行:
Properties p = System.getProperties(); p.list(System.out);
調用了一個名為getProperties()的static方法,用于獲得一個特殊的Properties對象,對系統的某些特征進行描述。list()屬于Properties的一個方法,可將內容發給我們選擇的任何流式輸出。也有一個save()方法,可用它將屬性列表寫入一個文件,以便日后用load()方法讀取。
盡管Properties類是從Hashtable繼承的,但它也包含了一個散列表,用于容納“默認”屬性的列表。所以假如沒有在主列表里找到一個屬性,就會自動搜索默認屬性。
Properties類亦可在我們的程序中使用(第17章的ClassScanner.java便是一例)。在Java庫的用戶文檔中,往往可以找到更多、更詳細的說明。
4、 再論枚舉器
我們現在可以開始演示Enumeration(枚舉)的真正威力:將穿越一個序列的操作與那個序列的基礎結構分隔開。在下面的例子里,PrintData類用一個Enumeration在一個序列中移動,并為每個對象都調用toString()方法。此時創建了兩個不同類型的集合:一個Vector和一個Hashtable。并且在它們里面分別填充Mouse和Hamster對象(本章早些時候已定義了這些類;注意必須先編譯HamsterMaze.java和WorksAnyway.java,否則下面的程序不能編譯)。由于Enumeration隱藏了基層集合的結構,所以PrintData不知道或者不關心Enumeration來自于什么類型的集合:
//: Enumerators2.java // Revisiting Enumerations import java.util.*; class PrintData { static void print(Enumeration e) { while(e.hasMoreElements()) System.out.println( e.nextElement().toString()); } } class Enumerators2 { public static void main(String[] args) { Vector v = new Vector(); for(int i = 0; i < 5; i++) v.addElement(new Mouse(i)); Hashtable h = new Hashtable(); for(int i = 0; i < 5; i++) h.put(new Integer(i), new Hamster(i)); System.out.println("Vector"); PrintData.print(v.elements()); System.out.println("Hashtable"); PrintData.print(h.elements()); } }
注意PrintData.print()利用了這些集合中的對象屬于Object類這一事實,所以它調用了toString()。但在解決自己的實際問題時,經常都要保證自己的Enumeration穿越某種特定類型的集合。例如,可能要求集合中的所有元素都是一個Shape(幾何形狀),并含有draw()方法。若出現這種情況,必須從Enumeration.nextElement()返回的Object進行下溯造型,以便產生一個Shape。
5、新集合
新的集合庫考慮到了“容納自己對象”的問題,并將其分割成兩個明確的概念:
(1) 集合(Collection):一組單獨的元素,通常應用了某種規則。在這里,一個List(列表)必須按特定的順序容納元素,而一個Set(集)不可包含任何重復的元素。相反,“包”(Bag)的概念未在新的集合庫中實現,因為“列表”已提供了類似的功能。
(2) 映射(Map):一系列“鍵-值”對(這已在散列表身上得到了充分的體現)。從表面看,這似乎應該成為一個“鍵-值”對的“集合”,但假若試圖按那種方式實現它,就會發現實現過程相當笨拙。這進一步證明了應該分離成單獨的概念。另一方面,可以方便地查看Map的某個部分。只需創建一個集合,然后用它表示那一部分即可。這樣一來,Map就可以返回自己鍵的一個Set、一個包含自己值的List或者包含自己“鍵-值”對的一個List。和數組相似,Map可方便擴充到多個“維”,毋需涉及任何新概念。只需簡單地在一個Map里包含其他Map(后者又可以包含更多的Map,以此類推)。
在通讀了本章以后,相信大家會真正理解它實際只有三個集合組件:Map,List和Set。而且每個組件實際只有兩、三種實現方式(注釋⑥),而且通常都只有一種特別好的方式。只要看出了這一點,集合就不會再令人生畏。
6、使用Collections
boolean add(Object) *保證集合內包含了自變量。如果它沒有添加自變量,就返回false(假)
boolean addAll(Collection) *添加自變量內的所有元素。如果沒有添加元素,則返回true(真)
void clear() *刪除集合內的所有元素
boolean contains(Object) 若集合包含自變量,就返回“真”
boolean containsAll(Collection) 若集合包含了自變量內的所有元素,就返回“真”
boolean isEmpty() 若集合內沒有元素,就返回“真”
Iterator iterator() 返回一個反復器,以用它遍歷集合的各元素
boolean remove(Object) *如自變量在集合里,就刪除那個元素的一個實例。如果已進行了刪除,就返回“真”
boolean removeAll(Collection) *刪除自變量里的所有元素。如果已進行了任何刪除,就返回“真”
boolean retainAll(Collection) *只保留包含在一個自變量里的元素(一個理論的“交集”)。如果已進行了任何改變,就返回“真”int size() 返回集合內的元素數量
Object[] toArray() 返回包含了集合內所有元素的一個數組
7、使用Lists
List(接口) 順序是List最重要的特性;它可保證元素按照規定的順序排列。List為Collection添加了大量方法,以便我們在List中部插入和刪除元素(只推薦對LinkedList這樣做)。List也會生成一個ListIterator(列表反復器),利用它可在一個列表里朝兩個方向遍歷,同時插入和刪除位于列表中部的元素(同樣地,只建議對LinkedList這樣做)
ArrayList* 由一個數組后推得到的List。作為一個常規用途的對象容器使用,用于替換原先的Vector。允許我們快速訪問元素,但在從列表中部插入和刪除元素時,速度卻嫌稍慢。一般只應該用ListIterator對一個ArrayList進行向前和向后遍歷,不要用它刪除和插入元素;與LinkedList相比,它的效率要低許多
LinkedList 提供優化的順序訪問性能,同時可以高效率地在列表中部進行插入和刪除操作。但在進行隨機訪問時,速度卻相當慢,此時應換用ArrayList。也提供了addFirst(),addLast(),getFirst(),getLast(),removeFirst()以及removeLast()(未在任何接口或基礎類中定義),以便將其作為一個規格、隊列以及一個雙向隊列使用
8、使用Sets
Set(接口) 添加到Set的每個元素都必須是獨一無二的;否則Set就不會添加重復的元素。添加到Set里的對象必須定義equals(),從而建立對象的唯一性。Set擁有與Collection完全相同的接口。一個Set不能保證自己可按任何特定的順序維持自己的元素
HashSet* 用于除非常小的以外的所有Set。對象也必須定義hashCode()
ArraySet 由一個數組后推得到的Set。面向非常小的Set設計,特別是那些需要頻繁創建和刪除的。對于小Set,與HashSet相比,ArraySet創建和反復所需付出的代價都要小得多。但隨著Set的增大,它的性能也會大打折扣。不需要HashCode()
TreeSet 由一個“紅黑樹”后推得到的順序Set。這樣一來,我們就可以從一個Set里提到一個順序集合
9、 使用Maps
Map(接口) 維持“鍵-值”對應關系(對),以便通過一個鍵查找相應的值
HashMap* 基于一個散列表實現(用它代替Hashtable)。針對“鍵-值”對的插入和檢索,這種形式具有最穩定的性能。可通過構建器對這一性能進行調整,以便設置散列表的“能力”和“裝載因子”
ArrayMap 由一個ArrayList后推得到的Map。對反復的順序提供了精確的控制。面向非常小的Map設計,特別是那些需要經常創建和刪除的。對于非常小的Map,創建和反復所付出的代價要比HashMap低得多。但在Map變大以后,性能也會相應地大幅度降低
TreeMap 在一個“紅-黑”樹的基礎上實現。查看鍵或者“鍵-值”對時,它們會按固定的順序排列(取決于Comparable或Comparator,稍后即會講到)。TreeMap最大的好處就是我們得到的是已排好序的結果。TreeMap是含有subMap()方法的唯一一種Map,利用它可以返回樹的一部分
總結
以上就是本文關于Java編程思想對象的容納實例詳解的全部內容,希望能對大家有所幫助,也希望大家對本站多多支持!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。