日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

    歡迎訪問 生活随笔!

    生活随笔

    當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

    编程问答

    HashMap HashTable HashSet区别剖析

    發(fā)布時間:2025/3/21 编程问答 52 豆豆
    生活随笔 收集整理的這篇文章主要介紹了 HashMap HashTable HashSet区别剖析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

    HashMap、HashSet、HashTable之間的區(qū)別是Java程序員的一個常見面試題目,在此僅以此博客記錄,并深入源代碼進行分析:

    在分析之前,先將其區(qū)別列于下面

    1:HashSet底層采用的是HashMap進行實現(xiàn)的,但是沒有key-value,只有HashMap的key set的視圖,HashSet不容許重復(fù)的對象

    2:Hashtable是基于Dictionary類的,而HashMap是基于Map接口的一個實現(xiàn)

    3:Hashtable里默認的方法是同步的,而HashMap則是非同步的,因此Hashtable是多線程安全的

    4:HashMap可以將空值作為一個表的條目的key或者value,HashMap中由于鍵不能重復(fù),因此只有一條記錄的Key可以是空值,而value可以有多個為空,但HashTable不允許null值(鍵與值均不行)

    5:內(nèi)存初始大小不同,HashTable初始大小是11,而HashMap初始大小是16

    6:內(nèi)存擴容時采取的方式也不同,Hashtable采用的是2*old+1,而HashMap是2*old。

    7:哈希值的計算方法不同,Hashtable直接使用的是對象的hashCode,而HashMap則是在對象的hashCode的基礎(chǔ)上還進行了一些變化

    源代碼分析:

    對于區(qū)別1,看下面的源碼

    ?

    [java]?view plaincopy
  1. //HashSet類的部份源代碼??
  2. public?class?HashSet<E>??
  3. ????extends?AbstractSet<E>??
  4. ????implements?Set<E>,?Cloneable,?java.io.Serializable??
  5. {???//用于類的序列化,可以不用管它??
  6. ????static?final?long?serialVersionUID?=?-5024744406713321676L;??
  7. ????//從這里可以看出HashSet類里面真的是采用HashMap來實現(xiàn)的??
  8. ????private?transient?HashMap<E,Object>?map;??
  9. ??
  10. ????//?Dummy?value?to?associate?with?an?Object?in?the?backing?Map??
  11. ????//這里是生成一個對象,生成這個對象的作用是將每一個鍵的值均關(guān)聯(lián)于此對象,以滿足HashMap的鍵值對??
  12. ????private?static?final?Object?PRESENT?=?new?Object();??
  13. ??
  14. ????/**?
  15. ?????*?Constructs?a?new,?empty?set;?the?backing?<tt>HashMap</tt>?instance?has?
  16. ?????*?default?initial?capacity?(16)?and?load?factor?(0.75).?
  17. ?????*/??
  18. ????//這里是一個構(gòu)造函數(shù),開構(gòu)生成一個HashMap對象,用來存放數(shù)據(jù)??
  19. ????public?HashSet()?{??
  20. ????map?=?new?HashMap<E,Object>();??
  21. ????}??
  22. 從上面的代碼中得出的結(jié)論是HashSet的確是采用HashMap來實現(xiàn)的,而且每一個鍵都關(guān)鍵同一個Object類的對象,因此鍵所關(guān)聯(lián)的值沒有意義,真正有意義的是鍵。而HashMap里的鍵是不允許重復(fù)的,因此1也就很容易明白了。

    ?

    對于區(qū)別2,繼續(xù)看源代碼如下

    ?

    [java]?view plaincopy
  23. //從這里可以看得出Hashtable是繼承于Dictionary,實現(xiàn)了Map接口??
  24. public?class?Hashtable<K,V>??
  25. ????extends?Dictionary<K,V>??
  26. ????implements?Map<K,V>,?Cloneable,?java.io.Serializable?{??
  27. [java]?view plaincopy
  28. //這里可以看出的是HashMap是繼承于AbstractMap類,實現(xiàn)了Map接口??
  29. //因此與Hashtable繼承的父類不同??
  30. public?class?HashMap<K,V>??
  31. ????extends?AbstractMap<K,V>??
  32. ????implements?Map<K,V>,?Cloneable,?Serializable??
  33. 區(qū)別3,找一個具有針對性的方法看看,這個方法就是put

    ?

    ?

    [java]?view plaincopy
  34. //Hashtable里的向集體增加鍵值對的方法,從這里可以明顯看到的是??
  35. //采用了synchronized關(guān)鍵字,這個關(guān)鍵字的作用就是用于線程的同步操作??
  36. //因此下面這個方法對于多線程來說是安全的,但這會影響效率?????
  37. public?synchronized?V?put(K?key,?V?value)?{??
  38. ????//?Make?sure?the?value?is?not?null??
  39. ????//如果值為空的,則會拋出異常??
  40. ????if?(value?==?null)?{??
  41. ????????throw?new?NullPointerException();??
  42. ????}??
  43. ??
  44. ????//?Makes?sure?the?key?is?not?already?in?the?hashtable.??
  45. ????Entry?tab[]?=?table;??
  46. ????//獲得鍵值的hashCode,從這里也可以看得出key!=null,否則的話會拋出異常的呦??
  47. ????int?hash?=?key.hashCode();??
  48. ????//獲取鍵據(jù)所在的哈希表的位置??
  49. ????int?index?=?(hash?&?0x7FFFFFFF)?%?tab.length;??
  50. ????//從下面這個循環(huán)中可以看出的是,內(nèi)部實現(xiàn)采用了鏈表,即桶狀結(jié)構(gòu)??
  51. ????for?(Entry<K,V>?e?=?tab[index]?;?e?!=?null?;?e?=?e.next)?{??
  52. ????????//如果向Hashtable中增加同一個元素時,則會重新更新元素的值???
  53. ????????if?((e.hash?==?hash)?&&?e.key.equals(key))?{??
  54. ????????????????V?old?=?e.value;??
  55. ????????????????e.value?=?value;??
  56. ????????????????return?old;??
  57. ????????}??
  58. ????}??
  59. ????//后面的暫時不用管它,大概的意思就是內(nèi)存的個數(shù)少于某個閥值時,進行重新分配內(nèi)存??
  60. ????modCount++;??
  61. ????if?(count?>=?threshold)?{??
  62. ????????//?Rehash?the?table?if?the?threshold?is?exceeded??
  63. ????????rehash();??
  64. ??
  65. ????????????tab?=?table;??
  66. ????????????index?=?(hash?&?0x7FFFFFFF)?%?tab.length;??
  67. ????}??
  68. [java]?view plaincopy
  69. //HashMap中的實現(xiàn)則相對來說要簡單的很多了,如下代碼??
  70. //這里的代碼中沒有synchronize關(guān)鍵字,即可以看出,這個關(guān)鍵函數(shù)不是線程安全的??
  71. ????public?V?put(K?key,?V?value)?{??
  72. ????//對于鍵是空時,將向Map中放值一個null-value構(gòu)成的鍵值對??
  73. ????//對值卻沒有進行判空處理,意味著可以有多個具有鍵,鍵所對應(yīng)的值卻為空的元素。??
  74. ????????if?(key?==?null)??
  75. ????????????return?putForNullKey(value);??
  76. ????//算出鍵所在的哈希表的位置??
  77. ????????int?hash?=?hash(key.hashCode());??
  78. ????????int?i?=?indexFor(hash,?table.length);??
  79. ????//同樣從這里可以看得出來的是采用的是鏈表結(jié)構(gòu),采用的是桶狀??
  80. ????????for?(Entry<K,V>?e?=?table[i];?e?!=?null;?e?=?e.next)?{??
  81. ????????????Object?k;??
  82. ????????????//對于向集體中增加具有相同鍵的情況時,這里可以看出,并不增加進去,而是進行更新操作??
  83. ????????????if?(e.hash?==?hash?&&?((k?=?e.key)?==?key?||?key.equals(k)))?{??
  84. ????????????????V?oldValue?=?e.value;??
  85. ????????????????e.value?=?value;??
  86. ????????????????e.recordAccess(this);??
  87. ????????????????return?oldValue;??
  88. ????????????}??
  89. ????????}??
  90. ????????//開始增加元素??
  91. ????????modCount++;??
  92. ????????addEntry(hash,?key,?value,?i);??
  93. ????????return?null;??
  94. ????}??
  95. 區(qū)別4在上面的代碼中,已經(jīng)分析了,可以再細看一下

    ?

    區(qū)別5內(nèi)存初化大小不同,看看兩者的源代碼:

    ?

    [java]?view plaincopy
  96. ?public?Hashtable()?{??
  97. ???//從這里可以看出,默認的初始化大小11,這里的11并不是11個字節(jié),而是11個Entry,這個Entry是??
  98. ???//實現(xiàn)鏈表的關(guān)鍵結(jié)構(gòu)??
  99. ???//這里的0.75代表的是裝載因子??
  100. this(11,?0.75f);??
  101. ?}??
  102. [java]?view plaincopy
  103. //這里均是一些定義??
  104. ?public?HashMap()?{??
  105. ?//這個默認的裝載因子也是0.75??
  106. ?????this.loadFactor?=?DEFAULT_LOAD_FACTOR;??
  107. ?//默認的痤為0.75*16??
  108. ?????threshold?=?(int)(DEFAULT_INITIAL_CAPACITY?*?DEFAULT_LOAD_FACTOR);??
  109. ?//這里開始是默認的初始化大小,這里大小是16??
  110. ?????table?=?new?Entry[DEFAULT_INITIAL_CAPACITY];??
  111. ?????init();??
  112. ?}??
  113. 從上面的代碼中,可以看出的是兩者的默認大小是不同的,一個是11,一個是16

    ?

    區(qū)別6內(nèi)存的擴容方式,看一看源代碼也是很清楚的,其實區(qū)別是不大的,看到網(wǎng)上一哥們寫的,說兩者有區(qū)別,其實真正深入源碼,區(qū)別真不大,一個是2*oldCapacity+1, 一個是2*oldCapacity,你說大嗎:)

    ?

    [java]?view plaincopy
  114. //Hashtable中調(diào)整內(nèi)存的函數(shù),這個函數(shù)沒有synchronize關(guān)鍵字,但是protected呦??
  115. protected?void?rehash()?{??
  116. ????//獲取原來的表大小??
  117. ????int?oldCapacity?=?table.length;??
  118. ????Entry[]?oldMap?=?table;??
  119. ??//設(shè)置新的大小為2*oldCapacity+1??
  120. ????int?newCapacity?=?oldCapacity?*?2?+?1;??
  121. ????//開設(shè)空間??
  122. ????Entry[]?newMap?=?new?Entry[newCapacity];??
  123. ??//以下就不用管了。。。??
  124. ????modCount++;??
  125. ????threshold?=?(int)(newCapacity?*?loadFactor);??
  126. ????table?=?newMap;??
  127. ??
  128. ????for?(int?i?=?oldCapacity?;?i--?>?0?;)?{??
  129. ????????for?(Entry<K,V>?old?=?oldMap[i]?;?old?!=?null?;?)?{??
  130. ????????Entry<K,V>?e?=?old;??
  131. ????????old?=?old.next;??
  132. ??
  133. ????????int?index?=?(e.hash?&?0x7FFFFFFF)?%?newCapacity;??
  134. ????????e.next?=?newMap[index];??
  135. ????????newMap[index]?=?e;??
  136. ????????}??
  137. ????}??
  138. ????}??
  139. [java]?view plaincopy
  140. //HashMap中要簡單的多了,看看就知道了??
  141. void?addEntry(int?hash,?K?key,?V?value,?int?bucketIndex)?{??
  142. Entry<K,V>?e?=?table[bucketIndex];??
  143. ???????table[bucketIndex]?=?new?Entry<K,V>(hash,?key,?value,?e);??
  144. ???????//如果超過了閥值??
  145. ???????if?(size++?>=?threshold)??
  146. ???????//就將大小設(shè)置為原來的2倍??
  147. ???????????resize(2?*?table.length);??
  148. ???}??
  149. 是吧,沒什么區(qū)別吧

    ?

    對于區(qū)別7的哈希值計算方法的不同,源碼面前,同樣是了無秘密

    ?

    [java]?view plaincopy
  150. //Hashtable中可以看出的是直接采用關(guān)鍵字的hashcode作為哈希值??
  151. int?hash?=?key.hashCode();??
  152. //然后進行模運算,求出所在嘩然表的位置???
  153. int?index?=?(hash?&?0x7FFFFFFF)?%?tab.length;??
  154. [java]?view plaincopy
  155. //HashMap中的實現(xiàn)??
  156. //這兩行代碼的意思是先計算hashcode,然后再求其在哈希表的相應(yīng)位置????????
  157. int?hash?=?hash(key.hashCode());??
  158. int?i?=?indexFor(hash,?table.length);??
  159. 上面的HashMap中可以看出關(guān)鍵在兩個函數(shù)hash與indexFor

    ?

    源碼如下:

    ?

    [java]?view plaincopy
  160. static?int?hash(int?h)?{??
  161. ????//?This?function?ensures?that?hashCodes?that?differ?only?by??
  162. ????//?constant?multiples?at?each?bit?position?have?a?bounded??
  163. ????//?number?of?collisions?(approximately?8?at?default?load?factor).??
  164. ????//這個我就不多說了,>>>這個是無符號右移運算符,可以理解為無符號整型??
  165. ????h?^=?(h?>>>?20)?^?(h?>>>?12);??
  166. ????return?h?^?(h?>>>?7)?^?(h?>>>?4);??
  167. }??
  168. [java]?view plaincopy
  169. //求位于哈希表中的位置??
  170. ?static?int?indexFor(int?h,?int?length)?{??
  171. ?????return?h?&?(length-1);??
  172. ?}?
  173. 轉(zhuǎn)載于:https://www.cnblogs.com/dangzhenjiuhao/p/5416470.html

    總結(jié)

    以上是生活随笔為你收集整理的HashMap HashTable HashSet区别剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。