HashMap通過hashcode快速搜索其內容,而TreeMap中的所有元素都保持壹定的固定順序。如果需要得到有序的結果,應該使用TreeMap(HashMap中元素的順序是不固定的)。
HashMap非線程安全樹圖非線程安全
線程安全
在Java中,線程安全壹般體現在兩個方面:
1,多個線程對同壹個java實例的訪問(讀取和修改)不會互相幹擾,這主要體現在關鍵字synchronized上。比如ArrayList和Vector,HashMap和Hashtable。
(後者在每個方法前都有壹個synchronized關鍵字)。如果interator中有壹個List對象,而其他線程刪除了壹個元素,問題就會出現。
2.每個線程都有自己的字段,不會在多個線程之間共享。主要體現在java.lang.ThreadLocal類中,沒有java關鍵字支持,比如static和transient。
1.抽象類和SortedMap接口
抽象地圖
抽象類:(HashMap繼承AbstractMap)重寫equals()和hashCode()方法,以確保兩個相等的映射返回相同的哈希代碼。如果兩個人
如果兩個映射大小相等,包含相同的鍵,並且每個鍵在兩個映射中具有相同的值,則這兩個映射相等。映射的散列碼是映射元素的散列碼的總和,其中每個元素是
地圖的實現。進入界面。因此,無論映射的內部順序如何,兩個相等的映射都會報告相同的哈希代碼。
SortedMap接口:(TreeMap
繼承自SortedMap)它用於保持鍵的有序。SortedMap接口為圖像的視圖(子集)提供訪問方法,包括兩個端點。除了排序對映射起作用。
鍵,處理SortedMap與處理SortedSet相同。添加到SortedMap實現類中的元素必須實現Comparable接口,否則必須給出它。
的構造函數提供了比較器接口的實現。TreeMap類是它唯壹的實現。
2.兩種傳統的Map實現
HashMap:基於哈希表的實現。HashCode()和equals()是用HashMap需要的key類明確定義的[hashCode()和equals()可以重寫]。為了優化HashMap空間的使用,可以調整初始容量和裝載因子。
(1)HashMap():構建壹個空散列圖像。
(2)HashMap(Map m):構建壹個hash映像,將映像m的所有映射相加。
(3)HashMap(int initialCapacity):構建壹個特定容量的空哈希映像。
(4) HashMap (int initial capacity,float load factor):構建壹個具有特定容量和負載因子的空哈希映像。
TreeMap:基於紅黑樹的實現。TreeMap沒有調整選項,因為樹總是處於平衡狀態。
(1)TreeMap():構建壹個空的圖像樹。
(2)TreeMap(Map m):建立壹棵圖像樹,並添加圖像m中的所有元素。
(3)TreeMap(比較器c):構建圖像樹,使用特定的比較器對關鍵詞進行排序。
(4)TreeMap(SortedMap s):構建壹棵圖像樹,將圖像樹S中的所有圖相加,使用與有序圖像S相同的比較器進行排序。
3.兩種常規地圖性能
HashMap:適用於在地圖中插入、刪除和定位元素。
Treemap:適合以自然順序或自定義順序遍歷鍵。
總結
HashMap通常比TreeMap快(由於樹和哈希表的數據結構),所以建議更多地使用HashMap,只有在需要對映射進行排序時才使用TreeMap。
進口?Java . util . hashmap;?
進口?Java . util . hashtable;?
進口?Java . util . iterator;?
進口?Java . util . map;?
進口?Java . util . treemap;?
公共?班級?HashMaps?{?
公共?靜電?作廢?main(String[]?args)?{?
地圖& lt弦,?字符串& gt?地圖?=?新的?HashMap & lt弦,?字符串& gt();?
map.put("a ",“AAA”);?
map.put("b ",“BBB”);?
map.put("c ",“CCC”);?
map.put("d ",“DDD”);?
叠代器& lt字符串& gt?叠代器?=?map.keySet()。叠代器();?
什麽時候?(iterator.hasNext())?{?
對象?鑰匙?=?iterator . next();?
system . out . println(" map . get(key)?是嗎?:"?+?map . get(key));?
}?
//?定義HashTable進行測試?
哈希表& lt字符串,?字符串& gt?tab?=?新的?哈希表& lt字符串,?字符串& gt();?
tab.put("a ",“AAA”);?
tab.put("b ",“BBB”);?
tab.put("c ",“CCC”);?
tab.put("d ",“DDD”);?
叠代器& lt字符串& gt?叠代器_1?=?tab.keySet()。叠代器();?
什麽時候?(iterator_1.hasNext())?{?
對象?鑰匙?=?iterator _ 1 . next();?
system . out . println(" tab . get(key)?是嗎?:"?+?tab . get(key));?
}?
樹形圖& lt弦,?字符串& gt?tmp?=?新的?樹形圖& lt字符串,?字符串& gt();?
tmp.put("a ",“AAA”);?
tmp.put("b ",“BBB”);?
tmp.put("c ",“CCC”);?
tmp.put("d ",“疾控中心”);?
叠代器& lt字符串& gt?叠代器_2?=?tmp.keySet()。叠代器();?
什麽時候?(iterator_2.hasNext())?{?
對象?鑰匙?=?iterator _ 2 . next();?
system . out . println(" tmp . get(key)?是嗎?:"?+?tmp . get(key));?
}?
}?
}運行結果如下:
map.get(鍵)是:ddd
map.get(key)是:bbb
map.get(key)是:ccc
map.get(key)是:aaa
tab.get(key)是:bbb
tab.get(鍵)是:aaa
tab.get(鍵)是:ddd
tab.get(key)是:ccc
tmp.get(鍵)是:aaa
tmp.get(key)是:bbb
tmp.get(key)是:ccc
tmp.get(鍵)是:cdc
HashMap結果是無序的,而TreeMap輸出結果是有序的。
接下來,我們將進入本文的主題。讓我們舉壹個例子來說明如何使用HashMap:
進口?Java . util . *;?
公共?班級?Exp1?{?
公共?靜電?作廢?main(String[]?args){?
HashMap?h 1 =新?HashMap();?
隨機?r 1 =新?random();?
為了什麽?(int?I = 0;我& lt1000;i++){?
整數?t =新?整數(r 1 . nextint(20));?
如果?(h1.containsKey(t))?
((Ctime)h1.get(t))。count++;?
不然呢?
h1.put(t,新的?ctime());?
}?
system . out . println(h 1);?
}?
}?
班級?Ctime{?
int?count = 1;?
公共?字符串?toString(){?
回歸?Integer.toString(計數);?
}?
}在HashMap中,通過get()獲取值,通過put()插入值,ContainsKey()用於檢查對象是否已經存在。可以看出,與ArrayList的操作相比,HashMap除了通過鍵索引其內容外,其他方面差別不大。
前面說過,HashMap是基於HashCode的,所有對象的超類對象中都有HashCode()方法,但是它和equals方法壹樣,並不適用於所有情況,所以我們需要重新編寫自己的HashCode()方法。這裏有壹個例子:
進口?Java . util . *;?
公共?班級?Exp2?{?
公共?靜電?作廢?main(String[]?args){?
HashMap?h2 =新?HashMap();?
為了什麽?(int?I = 0;我& lt10;i++)?
h2.put(新?元素(I),?新的?figure out());?
system . out . println(" H2:");?
System.out.println("Get?那個?結果?為了什麽?元素:“);?
元素?測試=新?元素(5);?
如果?(h2.containsKey(test))?
system . out . println((figure out)H2 . get(test));?
不然呢?
System.out.println("不是?發現”);?
}?
}?
班級?元素{?
int?號碼;?
公共?元素(int?n){?
數字= n;?
}?
}?
班級?算出{?
隨機?r =新?random();?
布爾?possible = r . nextdouble()& gt;0.5;?
公共?字符串?toString(){?
如果?(可能)?
回歸?“好的!”;?
不然呢?
回歸?“不可能!”;?
}?
}在這個例子中,Element用於索引對象Figureout,也就是說,Element是鍵,Figureout是值。在圖中
機器生成壹個浮點數,如果它大於0.5,打印“OK!”,否則打印“不可能!”。然後檢查對應於元素(3)的計算結果。
怎麽樣?
原來無論妳跑多少次,結果都是“沒找到”。也就是說,索引元素(3)不在HashMap中。這怎麽可能呢?
起源
我們慢慢說:元素的HashCode方法繼承自對象,對象中HashCode方法返回的HashCode對應當前位置。
地址,也就是說,對於不同的對象,即使它們的內容完全相同,HashCode()返回的值也會不同。這實際上違背了我們的意圖。因為我們在使用
HashMap,妳想通過使用相同內容的對象索引得到相同的目標對象,這就需要HashCode()在此時返回相同的值。在上面的例子中,我們期望
新元素(i) (i=5)和
Elementtest=newElement(5)相同,但實際上是兩個不同的對象。雖然它們的內容相同,但是它們在內存中的地址不同。所以這很自然
是的,上面的程序無法得到我們想象的結果。對元素類進行了以下更改:
班級?元素{?
int?號碼;?
公共?元素(int?n){?
數字= n;?
}?
公共?int?hashCode(){?
回歸?號碼;?
}?
公共?布爾?等於(對象?o){?
回歸?(o?instanceof?元素)?& amp& amp?(number==((Element)o)。號);?
}?
}這裏元素覆蓋了對象中的hashCode()和equals()方法。重寫hashCode(),使它的值為# as。
Hashcode返回,因此具有相同內容的對象將具有相同的hashcode。重寫equals()的目的是確定兩個鍵是否在HashMap中。
相等使得結果有意義(關於重寫equals()的內容,請參考我的另壹篇文章《對象類中的重寫方法》)。修改後的程序的結果如下:
h2:
獲取元素的結果:
不可能!
記住:如果妳想有效地使用HashMap,妳必須重寫它的HashCode()。
重寫HashCode()也有兩個原則:
[list=1]
您不必為每個不同的對象生成唯壹的hashcode,只要您的HashCode方法允許get()獲取put()放入的內容。即“不為壹”的原則。
生長
生成hashcode的算法使hashcode的值盡可能分散,不希望很多hashcode集中在壹個範圍內,有利於提高HashMap的性能。即“
分權原則”。至於第二個原則的具體原因,有興趣的人可以參考布魯斯·埃凱爾的思想。
Java,裏面有HashMap內部實現原理的介紹,這裏就不贅述了。
掌握了這兩條原則,妳就可以用HashMap編寫自己的程序了。
不知道大家有沒有註意到,java.lang.Object中提供的三個方法:clone()、equals()和hashCode()很典型,但是也有很多。
它們只是從對象的地址得到結果。這要求我們在自己的程序中重寫它們。事實上,java類庫已經在千千重寫了數千個這樣的方法。使用
面向對象的多態性覆蓋,Java的設計者優雅地構造了Java的結構,這也體現了Java是純OOP語言的特點。