說一個幾個月前遇到的一個面試題,當時沒答出來,被刷下來了,後來後悔不已。可是歸根結底就是個人Java基礎仍是欠缺。今天在項目中再次遇到,咱們就來回顧一下。java
Java中HashMap與HashTable的區別:面試
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()方法來區別這兩種狀況。 ide
一些資料建議,當須要同步時,用Hashtable,反之用HashMap。可是,由於在須要時,HashMap能夠被同步,HashMap的功能比Hashtable的功能更多,並且它不是基於一個陳舊的類的,因此有人認爲,在各類狀況下,HashMap都優先於Hashtable。 函數
關於Properties 性能
有時侯,你可能想用一個hashtable來映射key的字符串到value的字符串。DOS、Windows和Unix中的環境字符串就有一些例子,如key的字符串PATH被映射到value的字符串C:\WINDOWS;C:\WINDOWS\SYSTEM。Hashtables是表示這些的一個簡單的方法,但Java提供了另一種方法。 ui
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來加速你的處理了。
咱們再來看下JavaAPI中是怎麼說的。
java.lang.Object java.util.AbstractMap<K,V> java.util.HashMap<K,V>
K
- 此映射所維護的鍵的類型
V
- 所映射值的類型
public class HashMap<K,V>
基於哈希表的 Map 接口的實現。此實現提供全部可選的映射操做,並容許使用 null 值和 null 鍵。(除了非同步和容許使用 null 以外,HashMap 類與 Hashtable 大體相同。)此類不保證映射的順序,特別是它不保證該順序恆久不變。
此實現假定哈希函數將元素適當地分佈在各桶之間,可爲基本操做(get 和 put)提供穩定的性能。迭代 collection 視圖所需的時間與 HashMap 實例的「容量」(桶的數量)及其大小(鍵-值映射關係數)成比例。因此,若是迭代性能很重要,則不要將初始容量設置得過高(或將加載因子設置得過低)。
HashMap 的實例有兩個參數影響其性能:初始容量 和加載因子。容量 是哈希表中桶的數量,初始容量只是哈希表在建立時的容量。加載因子 是哈希表在其容量自動增長以前能夠達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 rehash 操做(即重建內部數據結構),從而哈希表將具備大約兩倍的桶數。
一般,默認加載因子 (.75) 在時間和空間成本上尋求一種折衷。加載因子太高雖然減小了空間開銷,但同時也增長了查詢成本(在大多數 HashMap 類的操做中,包括 get 和 put 操做,都反映了這一點)。在設置初始容量時應該考慮到映射中所需的條目數及其加載因子,以便最大限度地減小 rehash 操做次數。若是初始容量大於最大條目數除以加載因子,則不會發生 rehash 操做。
若是不少映射關係要存儲在 HashMap 實例中,則相對於按需執行自動的 rehash 操做以增大表的容量來講,使用足夠大的初始容量建立它將使得映射關係能更有效地存儲。
注意,此實現不是同步的。若是多個線程同時訪問一個哈希映射,而其中至少一個線程從結構上修改了該映射,則它必須 保持外部同步。(結構上的修改是指添加或刪除一個或多個映射關係的任何操做;僅改變與實例已經包含的鍵關聯的值不是結構上的修改。)這通常經過對天然封裝該映射的對象進行同步操做來完成。若是不存在這樣的對象,則應該使用 Collections.synchronizedMap
方法來「包裝」該映射。最好在建立時完成這一操做,以防止對映射進行意外的非同步訪問,以下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
由全部此類的「collection 視圖方法」所返回的迭代器都是快速失敗 的:在迭代器建立以後,若是從結構上對映射進行修改,除非經過迭代器自己的 remove 方法,其餘任什麼時候間任何方式的修改,迭代器都將拋出 ConcurrentModificationException
。所以,面對併發的修改,迭代器很快就會徹底失敗,而不冒在未來不肯定的時間發生任意不肯定行爲的風險。
注意,迭代器的快速失敗行爲不能獲得保證,通常來講,存在非同步的併發修改時,不可能做出任何堅定的保證。快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。所以,編寫依賴於此異常的程序的作法是錯誤的,正確作法是:迭代器的快速失敗行爲應該僅用於檢測程序錯誤。
此類是 Java Collections Framework 的成員。
java.lang.Object java.util.Dictionary<K,V> java.util.Hashtable<K,V>
public class Hashtable<K,V>
此類實現一個哈希表,該哈希表將鍵映射到相應的值。任何非 null
對象均可以用做鍵或值。
爲了成功地在哈希表中存儲和獲取對象,用做鍵的對象必須實現 hashCode
方法和 equals
方法。
Hashtable
的實例有兩個參數影響其性能:初始容量 和加載因子。容量 是哈希表中桶 的數量,初始容量 就是哈希表建立時的容量。注意,哈希表的狀態爲 open:在發生「哈希衝突」的狀況下,單個桶會存儲多個條目,這些條目必須按順序搜索。加載因子 是對哈希表在其容量自動增長以前能夠達到多滿的一個尺度。初始容量和加載因子這兩個參數只是對該實現的提示。關於什麼時候以及是否調用 rehash 方法的具體細節則依賴於該實現。
一般,默認加載因子(.75)在時間和空間成本上尋求一種折衷。加載因子太高雖然減小了空間開銷,但同時也增長了查找某個條目的時間(在大多數 Hashtable 操做中,包括 get 和 put 操做,都反映了這一點)。
初始容量主要控制空間消耗與執行 rehash
操做所須要的時間損耗之間的平衡。若是初始容量大於 Hashtable 所包含的最大條目數除以加載因子,則永遠 不會發生 rehash
操做。可是,將初始容量設置過高可能會浪費空間。
若是不少條目要存儲在一個 Hashtable
中,那麼與根據須要執行自動 rehashing 操做來增大表的容量的作法相比,使用足夠大的初始容量建立哈希表或許能夠更有效地插入條目。
下面這個示例建立了一個數字的哈希表。它將數字的名稱用做鍵:
Hashtable<String, Integer> numbers = new Hashtable<String, Integer>(); numbers.put("one", 1); numbers.put("two", 2); numbers.put("three", 3);
要獲取一個數字,可使用如下代碼:
}Integer n = numbers.get("two"); if (n != null) { System.out.println("two = " + n); }
由全部類的「collection 視圖方法」返回的 collection 的 iterator 方法返回的迭代器都是快速失敗 的:在建立 Iterator 以後,若是從結構上對 Hashtable 進行修改,除非經過 Iterator 自身的 remove 方法,不然在任什麼時候間以任何方式對其進行修改,Iterator 都將拋出ConcurrentModificationException
。所以,面對併發的修改,Iterator 很快就會徹底失敗,而不冒在未來某個不肯定的時間發生任意不肯定行爲的風險。由 Hashtable 的鍵和元素方法返回的 Enumeration 不 是快速失敗的。
注意,迭代器的快速失敗行爲沒法獲得保證,由於通常來講,不可能對是否出現不一樣步併發修改作出任何硬性保證。快速失敗迭代器會盡最大努力拋出 ConcurrentModificationException。所以,爲提升這類迭代器的正確性而編寫一個依賴於此異常的程序是錯誤作法:迭代器的快速失敗行爲應該僅用於檢測程序錯誤。
從Java 2 平臺 v1.2起,此類就被改進以實現 Map
接口,使它成爲 Java Collections Framework 中的一個成員。不像新的 collection 實現,Hashtable
是同步的
StringBuilder與StringBuffer的區別
1. 在執行速度方面的比較:StringBuilder > StringBuffer
2. StringBuffer與StringBuilder,他們是字符串變量,是可改變的對象,每當咱們用它們對字符串作操做時,其實是在一個對象上操做的,不像String同樣建立一些對象進行操做,因此速度就快了。
3. StringBuilder:線程非安全的 StringBuffer:線程安全的 當咱們在字符串緩衝去被多個線程使用是,JVM不能保證StringBuilder的操做是安全的,雖然他的速度最快,可是能夠保證StringBuffer是能夠正確操做的。固然大多數狀況下就是咱們是在單線程下進行的操做,因此大多數狀況下是建議用StringBuilder而不用StringBuffer的,就是速度的緣由。
對於三者使用的總結:
1.若是要操做少許的數據用 = String
2.單線程操做字符串緩衝區 下操做大量數據 = StringBuilder
3.多線程操做字符串緩衝區 下操做大量數據 = StringBuffer