Java Hashmap 和 Hashtable

來源:互聯網
上載者:User

轉載自:

http://zztu.javaeye.com/blog/173964

該部落格轉載自:

http://user.qzone.qq.com/33658071/blog/1193889677


Hashtables提供了一個很有用的方法可以使應用程式的效能達到最佳。
Hashtables(雜湊表)在電腦領域中已不是一個新概念了。它們是用來加快電腦的處理速度的,用當今的標準來處理,速度非常慢,而它們可以讓你在查詢許多資料條目時,很快地找到一個特殊的條目。儘管現代的機器速度已快了幾千倍,但是為了得到應用程式的最佳效能,hashtables仍然是個很有用的方法。
設想一下,你有一個包含約一千條記錄的資料檔案??比如一個小企業的客戶記錄還有一個程式,它把記錄讀到記憶體中進行處理。每個記錄包含一個唯一的五位元的客戶ID號、客戶名字、地址、帳戶結餘等等。假設記錄不是按客戶ID號順序分類的,所以,如果程式要將客戶號作為“key” 來尋找一個特殊的客戶記錄,唯一的尋找方法就是連續地搜尋每個記錄。有時侯,它會很快找到你需要的記錄;但有時侯,在程式找到你需要的記錄前,它幾乎已搜尋到了最後一條記錄。如果要在1,000條記錄中搜尋,那麼尋找任何一條記錄都需要程式平均查核500.5 ((1000 + 1 )/2)條記錄。如果你常需要尋找資料,你應該需要一個更快的方法來找到一條記錄。
一種加快搜尋的方法就是把記錄分成幾段,這樣,你就不用搜尋一個很大的列表了,而是搜尋幾個短的列表。對於我們數字式的客戶ID號,你可以建10個列表??以0開頭的ID號組成一個列表,以1開頭的ID號組成一個列表,依此類推。那麼要尋找客戶ID號38016,你只需要搜尋以3開頭的列表就行了。如果有1,000條記錄,每個列表的平均長度為100(1,000條記錄被分成10個列表),那麼搜尋一條記錄的平均比較次數就降到了約50(見圖1)。
當然,如果約十分之一的客戶號是以0開頭的,另外十分之一是以1開頭的,等等,那麼這種方法會很適合。如果90%的客戶號以0開頭,那麼那個列表就會有900條記錄,每次尋找平均需要進行450次比較。另外,程式需要執行的搜尋有90%都是針對以0開頭的號碼的。因此,平均比較數就大大超過簡單數學運算的範圍了。
如果我們可以按這樣一種方式在我們的列表中分配記錄,情況就會好一些,即每個列表約有相同條目的記錄,而不管索引值中數位分布。我們需要一種方法能夠把客戶號碼混合到一起並更好地分布結果。例如,我們可以取號碼中的每位元,乘以某個大的數(隨著數字位置的不同而不同), 然後將結果相加產生一個總數,把這個數除以10,並將餘數作為索引值(index)。當讀入記錄時,程式在客戶號碼上運行這個雜湊(hash) 函數來確定記錄屬於哪個列表。當使用者需要查詢時,將同一個雜湊函數作為一個“key”用於客戶號碼,這樣就可以搜尋正確的列表了。 像這樣的一個資料結構就稱為一個雜湊表(hashtable)。
Java中的Hashtables
Java包含兩個類,java.util.Hashtable 和java.util.HashMap,它們提供了一個多種用途的hashtable機制。這兩個類很相似,通常提供相同的公有介面。但它們的確有一些重要的不同點,我在後面會講到。
Hashtable和HashMap對象可以讓你把一個key和一個value結合起來,並用put() 方法把這對key/value輸入到表中。然後你可以通過調用get()方法,把key作為參數來得到這個value(值)。只要滿足兩個基本的要求,key和value可以是任何對象。注意,因為key和value必須是對象,所以原始類型(primitive types)必須通過運用諸如Integer(int)的方法轉換成對象。
為了將一個特定類的對象用做一個key,這個類必須提供兩個方法,equals() 和 hashCode()。這兩個方法在java.lang.Object中,所以所有的類都可以繼承這兩個方法;但是,這兩個方法在Object類中的實現一般沒什麼用,所以你通常需要自己重載這兩個方法。
Equals()方法把它的對象同另一個對象進行比較,如果這兩個對象代表相同的資訊,則返回true。該方法也查看並確保這兩個對象屬於相同的類。如果兩個參照對象是完全一樣的對象,Object.equals()返回true,這就說明了為什麼這個方法通常不是很適合的原因。在大多數情況下,你需要一個方法來一個欄位一個欄位地進行比較,所以我們認為代表相同資料的不同對象是相等的。
HashCode()方法通過運用對象的內容執行一個雜湊函數來產生一個int值。Hashtable和HashMap用這個值來算出一對key/value位於哪個bucket(雜湊元)(或列表)中。
作為例子,我們可以查看一下String 類,因為它有自己的方法來實現這兩個方法。String.equals()對兩個String對象一個字元一個字元地進行比較,如果字串是相同的,則返回true:
String myName = "Einstein";
// The following test is
// always true
if ( myName.equals("Einstein") )
{ ...
String.hashCode()在一個字串上運行雜湊函數。字串中每個字元的數字代碼都乘以31,結果取決於字串中字元的位置。然後將這些計算的結果相加,得到一個總數。這個過程似乎很複雜,但是它確保能夠更好地分布值。它也證明了你在開發你自己的hashCode()方法時,能夠走多遠,確信結果是唯一的。
例如,假設我要用一個hashtable來實現一個書的目錄,把書的ISBN號碼作為搜尋鍵來進行搜尋。我可以用String類來承載細節,並準備好了equals()和hashCode()方法(見列表1)。我們可以用put()方法添加成對的key/value到hashtable中(見列表2)。
Put()方法接受兩個參數,它們都屬於Object類型。第一個參數是key;第二個參數是value。Put()方法調用key的hashCode()方法,用表中的列表數來除這個結果。把餘數作為索引值來確定該條記錄添加到哪個列表中。注意,key在表中是唯一的;如果你用一個已經存在的key來調用put(),匹配的條目就被修改了,因此它參照的是一個新的值,而舊的值被返回了(當key在表中不存在時,put()返回空值)。
要讀取表中的一個值,我們把搜尋鍵用於get()方法。它返回一個轉換到正確類型的Object參照:BookRecord br =
(BookRecord)isbnTable.get(
"0-345-40946-9");
System.out.println(
"Author: " + br.author
+ " Title: " + br.title);
另一個有用的方法是remove(),其用法同get()幾乎一樣,它把條目從表中刪除,並返回給調用程式。
你自己的類
如果你想把一個原始類型用做一個key,你必須建立一個同等類型的對象。例如,如果你想用一個整數key,你應該用構造器Integer(int)從整數中產生一個對象。所有的封裝類??如Integer、Float和Boolean都把原始值看做是對象,它們重載了equals()和hashCode()方法,因此,它們可以被用做key。JDK中提供的許多其它的類也是這樣的(甚至Hashtable和HashMap類都實現它們自己的equals()和hashCode()方法),但你把任何類的對象用做hashtable keys前,應該查看檔案。查看類的來源,看看equals()和hashCode()是如何?的,也很有必要。例如,Byte、Character、Short和Integer都返回所代表的整數值作為雜湊碼。這可能適合,也可能不適合你的需求。
在Java中運用Hashtables
如果你想建立一個hashtable,這個hashtable運用你自己定義的一個類的對象作為key,那麼你應該確信這個類的equals()和hashCode()方法提供有用的值。首先查看你擴充的類,確定它的實現是否滿足你的需求。如果沒有,你應該重載方法。
任何equals()方法的基本設計約束是,如果傳遞給它的對象屬於同一個類,而且它的資料欄位設定為表示同樣資料的值,那麼它就應該返回true。你也應該確信,如果傳遞一個空的參數給該方法,那麼你的代碼返回false:public boolean equals(Object o)
{
if ( (o == null)
|| !(o instanceof myClass))
{
return false;
}
// Now compare data fields...
另外,在設計一個hashCode()方法時,應該記住一些規則。首先,該方法必須為一個特定的對象返回相同的值,而不管這個方法被調用了多少次(當然,只要對象的內容在調用之間沒有改變,在將一個對象用做一個hashtable的key時,應該避免這一點)。第二,如果由你的equals()方法定義的兩個對象是相等的,那麼它們也必鬚生成相同的雜湊碼。第三,這更像是一個方針,而不是一個原則,你應該設法設計方法,使它為不同的對象內容產生不同的結果。如果偶爾不同的對象正好產生了相同的雜湊碼,這也不要緊。但是,如果該方法只能返回範圍在1到10的值,那麼只能用10個列表,而不管在hashtable中有多少個列表。
在設計equals()和hashCode()時,另一個要記住的因素是效能問題。每次調用put()或get(),都包括調用hashCode()來尋找正確的列表,當get()掃描列表來尋找key時,它為列表中的每個元素調用equals()。實現這些方法使它們儘可能快而有效地運行,尤其當你打算使你的類公開可用時,因為其它的使用者可能想在執行速度很重要的情況下,在高效能的應用程式中運用你的類。
Hashtable效能
影響hashtable功效的主要因素就是表中列表的平均長度,因為平均搜尋時間與這個平均長度直接相關。很顯然,要減小平均長度,你必須增加hashtable中列表的數量;如果列表數量非常大,以至於大多數列表或所有列表只包含一條記錄,你就會獲得最佳的搜尋效率。然而,這樣做可能太過分了。如果你的hashtable的列表數遠遠多於資料條目,那你就沒有必要做這樣的記憶體花費了,而在一些情況下,人們也不可能接受這樣的做法。
在我們前面的例子中,我們預Crowdsourced Security Testing道我們有多少條記錄1,000。知道這點後,我們就可以決定我們的hashtable應該包含多少個列表,以便達成搜尋速度和記憶體使用量效率之間最好的折中方式。然而,在許多情況下,你預先不知道你要處理多少條記錄;資料被讀取的檔案可能會不斷擴大,或者記錄的數量可能一天一天地發生很大的變化。
隨著條目的增加,Hashtable和HashMap類通過動態地擴充表來處理這個問題。這兩個類都有接受表中列表最初數量的構造器,和一個作為參數的負載係數(load factor):public Hashtable(
int initialCapacity,
float loadFactor)
public HashMap(
int initialCapacity,
float loadFactor)
將這兩個數相乘計算出一個臨界值。每次給雜湊表添加一個新的條目時,計數就被更新,當計數超過臨界值時,表被重新設定(rehash)。(列表數量增加到以前數量的兩倍加1,所有的條目轉移到正確的列表中。)預設的構造器設定最初的容量為11,負載係數是0.75,所以臨界值是8。當第九條記錄被添加到表中時,就重新調整雜湊表,使其有23個列表,新的臨界值將是17(23*0.75的整數部分)。你可以看到,負載係數是雜湊表中平均列表數量的上限,這就意味著,在預設情況下,雜湊表很少會有許多包含不只一條記錄的列表。比較我們最初的例子,在那個例子中,我們有1,000條記錄,分布在10個列表中。如果我們用預設值,這個表將會擴充到含有1,500多個列表。但你可以控制這點。如果用負載係數相乘的列表數量大於你處理的條目數,那麼表永遠不會重製,所以我們可以仿效下面的例子:// Table will not rehash until it
// has 1,100 entries (10*110):
Hashtable myHashTable =
new Hashtable(10, 110.0F);
你可能不想這麼做,除非你沒有為空白的列表節省記憶體,而且不介意額外的搜尋時間,這可能在嵌入系統中會出現這種情況。然而,這種方法可能很有用,因為重新設定很佔用計算時間,而這種方法可以保證永遠不會發生重新設定這種情況。
注意,雖然調用put()可以使表增大(列表數量增加),調用remove()不會有相反的結果。所以,如果你有一個大的表,而且從中刪除了大部分條目,結果你會有一個大的但是大部分是空的表。
Hashtable和HashMap
Hashtable和HashMap類有三個重要的不同之處。第一個不同主要是曆史原因。Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map介面的一個實現。
也許最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。這就意味著,雖然你可以不用採取任何特殊的行為就可以在一個多線程的應用程式中用一個Hashtable,但你必須同樣地為一個HashMap提供外同步。一個方便的方法就是利用Collections類的靜態synchronizedMap()方法,它建立一個安全執行緒的Map對象,並把它作為一個封裝的對象來返回。這個對象的方法可以讓你同步訪問潛在的HashMap。這麼做的結果就是當你不需要同步時,你不能切斷Hashtable中的同步(比如在一個單線程的應用程式中),而且同步增加了很多處理費用。
第三點不同是,只有HashMap可以讓你將空值作為一個表的條目的key或value。HashMap中只有一條記錄可以是一個空的key,但任意數量的條目可以是空的value。這就是說,如果在表中沒有發現搜尋鍵,或者如果發現了搜尋鍵,但它是一個空的值,那麼get()將返回null。如果有必要,用containKey()方法來區別這兩種情況。
一些資料建議,當需要同步時,用Hashtable,反之用HashMap。但是,因為在需要時,HashMap可以被同步,HashMap的功能比Hashtable的功能更多,而且它不是基於一個陳舊的類的,所以有人認為,在各種情況下,HashMap都優先於Hashtable。
關於Properties
有時侯,你可能想用一個hashtable來映射key的字串到value的字串。DOS、Windows和Unix中的環境字串就有一些例子,如key的字串PATH被映射到value的字串C:/WINDOWS;C:/WINDOWS/SYSTEM。Hashtables是表示這些的一個簡單的方法,但Java提供了另外一種方法。
Java.util.Properties類是Hashtable的一個子類,設計用於String keys和values。Properties對象的用法同Hashtable的用法相象,但是類增加了兩個節省時間的方法,你應該知道。
Store()方法把一個Properties對象的內容以一種可讀的形式儲存到一個檔案中。Load()方法正好相反,用來讀取檔案,並設定Properties對象來包含keys和values。
注意,因為Properties擴充了Hashtable,你可以用超類的put()方法來添加不是String對象的keys和values。這是不可取的。另外,如果你將store()用於一個不包含String對象的Properties對象,store()將失敗。作為put()和get()的替代,你應該用setProperty()和getProperty(),它們用String參數。
好了,我希望你現在可以知道如何用hashtables來加速你的處理了
Map借口和Collection沒有關係所以沒有add()方法。
PS:
HashMap對key進行散列。
添加元素採用put()方法,得到元素採用get(key)方法。
keySet()返回map中的key視圖,傳回值為set,可以通過迭代器輸出。
values()返回map中的value視圖,傳回值為collection,可以通過迭代器輸出。
entrySet()。返回map視圖傳回值為set,可以通過迭代器輸出。
這裡要提醒注意的是 這個set中的每個元素都是Map.Entry類型,每個元素都有getKey()和getValue()可以分別獲得 key和value 。
2.TreeMap
TreeMap按照key進行排序
HashMap和TreeMap的比較
和Set類似,HashMap的速度通常都比TreeMap快,只有在需要排序的功能的時候,才使用TreeMap。
1 Vector:用ArrayList代替Vector。
2 Hashtable:用HashMap代替Hashtable。
3 Satck:用LinkedList代替Stack,,主要是因為這個類繼承了vector的elementAt(index)方法,丟棄了棧的特性,我們可以採用LinkedList來類比 stack和quene。
4 用ArrayList代替Vector。
Hashtable:用HashMap代替Hasht 和用HashMap代替Hashtable。有相同的原因
關於原因的分析參看tutorial1.5下面是摘錄。
Vector中的所有方法都是同步的,這樣會降低運行速度,在多線程環境下要用到ArrayList方法synchronizedList(List list)
1散列表
2 散列表又稱為雜湊表。散列表演算法的基本思想是:
以結點的關鍵字為自變數,通過一定的函數關係(散列函數)計算出對應的函數值,以這個值作為該結點儲存在散列表中的地址。
8 當散列表中的元素存放太滿,就必須進行再散列,將產生一個新的散列表,所有元素存放到新的散列表中,原先的散列表將被刪除。在Java語言中,通過負載因子(load factor)來決定何時對散列表進行再散列。例如:如果負載因子是0.75,當散列表中已經有75%的位置已經放滿,那麼將進行再散列。
9 負載因子越高(越接近1.0),記憶體的使用效率越高,元素的尋找時間越長。負載因子越低(越接近0.0),元素的尋找時間越短,記憶體浪費越多。
10 HashSet類的預設負載因子是0.75。
zztu:
1.HashMap和Hashtable實現介面不一樣,一個是新的Map,一個原Dictionary;
2.Hashtable是線程同步,HashMap不是,在多線程編碼的時候需要Collections.synchronizedMaps來同步,所以一般情況下HashMap的效率優於Hashtable
3.HashMap可以用null做主鍵和值

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.