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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

HashSet源码解析

發(fā)布時(shí)間:2025/3/21 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HashSet源码解析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前面我們花了一定的篇幅學(xué)習(xí)了HashMap的一些底層原理,以及簡(jiǎn)單了解了HashSet和HashMap兩種集合的淵源,現(xiàn)在我們從HashSet源碼入手,來(lái)學(xué)習(xí)HashSet更細(xì)節(jié)的地方。

對(duì)于HashSet而言,它是基于HashMap實(shí)現(xiàn)的。HashSet底層采用HashMap來(lái)保存元素,因此HashSet底層其實(shí)比較簡(jiǎn)單。

package java.util;

public class HashSet<E>
? ? extends AbstractSet<E>
? ? implements Set<E>, Cloneable, java.io.Serializable
{
? ? static final long serialVersionUID = -5024744406713321676L;

? ? // HashSet是通過(guò)map(HashMap對(duì)象)保存內(nèi)容的
? ? private transient HashMap<E,Object> map;

? ? // 定義一個(gè)虛擬的Object PRESENT是向map中插入key-value對(duì)應(yīng)的value
? ? // 因?yàn)镠ashSet中只需要用到key,而HashMap是key-value鍵值對(duì);
? ? // 所以,向map中添加鍵值對(duì)時(shí),鍵值對(duì)的值固定是PRESENT
? ? private static final Object PRESENT = new Object();

? ? // 默認(rèn)構(gòu)造函數(shù) 底層創(chuàng)建一個(gè)HashMap
? ? public HashSet() {
? ? ? ? // 調(diào)用HashMap的默認(rèn)構(gòu)造函數(shù),創(chuàng)建map
? ? ? ? map = new HashMap<E,Object>();
? ? }

? ? // 帶集合的構(gòu)造函數(shù)
? ? public HashSet(Collection<? extends E> c) {
? ? ? ? // 創(chuàng)建map。
? ? ? ? // 為什么要調(diào)用Math.max((int) (c.size()/.75f) + 1, 16),從 (c.size()/.75f) + 1 和 16 中選擇一個(gè)比較大的樹(shù)呢? ? ? ? ?
? ? ? ? // 首先,說(shuō)明(c.size()/.75f) + 1
? ? ? ? // ? 因?yàn)閺腍ashMap的效率(時(shí)間成本和空間成本)考慮,HashMap的加載因子是0.75。
? ? ? ? // ? 當(dāng)HashMap的“閾值”(閾值=HashMap總的大小*加載因子) < “HashMap實(shí)際大小”時(shí),
? ? ? ? // ? 就需要將HashMap的容量翻倍。
? ? ? ? // ? 所以,(c.size()/.75f) + 1 計(jì)算出來(lái)的正好是總的空間大小。
? ? ? ? // 接下來(lái),說(shuō)明為什么是 16 。
? ? ? ? // ? HashMap的總的大小,必須是2的指數(shù)倍。若創(chuàng)建HashMap時(shí),指定的大小不是2的指數(shù)倍;
? ? ? ? // ? HashMap的構(gòu)造函數(shù)中也會(huì)重新計(jì)算,找出比“指定大小”大的最小的2的指數(shù)倍的數(shù)。
? ? ? ? // ? 所以,這里指定為16是從性能考慮。避免重復(fù)計(jì)算。
? ? ? ? map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
? ? ? ? // 將集合(c)中的全部元素添加到HashSet中
? ? ? ? addAll(c);
? ? }

? ? // 指定HashSet初始容量和加載因子的構(gòu)造函數(shù)
? ? public HashSet(int initialCapacity, float loadFactor) {
? ? ? ? map = new HashMap<E,Object>(initialCapacity, loadFactor);
? ? }

? ? // 指定HashSet初始容量的構(gòu)造函數(shù)
? ? public HashSet(int initialCapacity) {
? ? ? ? map = new HashMap<E,Object>(initialCapacity);
? ? }

? ? HashSet(int initialCapacity, float loadFactor, boolean dummy) {
? ? ? ? map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
? ? }

? ? // 返回HashSet的迭代器
? ? public Iterator<E> iterator() {
? ? ? ? // 實(shí)際上返回的是HashMap的“key集合的迭代器”
? ? ? ? return map.keySet().iterator();
? ? }
? ?//調(diào)用HashMap的size()方法返回Entry的數(shù)量,得到該Set里元素的個(gè)數(shù)
? ? public int size() {
? ? ? ? return map.size();
? ? }
? ?//調(diào)用HashMap的isEmpty()來(lái)判斷HaspSet是否為空
? ?//HashMap為null。對(duì)應(yīng)的HashSet也為空
? ? public boolean isEmpty() {
? ? ? ? return map.isEmpty();
? ? }
? ? //調(diào)用HashMap的containsKey判斷是否包含指定的key
? ? //HashSet的所有元素就是通過(guò)HashMap的key來(lái)保存的
? ? public boolean contains(Object o) {
? ? ? ? return map.containsKey(o);
? ? }

? ? // 將元素(e)添加到HashSet中,也就是將元素作為Key放入HashMap中
? ? public boolean add(E e) {
? ? ? ? return map.put(e, PRESENT)==null;
? ? }

? ? // 刪除HashSet中的元素(o),其實(shí)是在HashMap中刪除了以o為key的Entry
? ? public boolean remove(Object o) {
? ? ? ? return map.remove(o)==PRESENT;
? ? }
? ? ?//清空HashMap的clear方法清空所有Entry
? ? public void clear() {
? ? ? ? map.clear();
? ? }

? ? // 克隆一個(gè)HashSet,并返回Object對(duì)象
? ? public Object clone() {
? ? ? ? try {
? ? ? ? ? ? HashSet<E> newSet = (HashSet<E>) super.clone();
? ? ? ? ? ? newSet.map = (HashMap<E, Object>) map.clone();
? ? ? ? ? ? return newSet;
? ? ? ? } catch (CloneNotSupportedException e) {
? ? ? ? ? ? throw new InternalError();
? ? ? ? }
? ? }

? ? // java.io.Serializable的寫(xiě)入函數(shù)
? ? // 將HashSet的“總的容量,加載因子,實(shí)際容量,所有的元素”都寫(xiě)入到輸出流中
? ? private void writeObject(java.io.ObjectOutputStream s)
? ? ? ? throws java.io.IOException {
? ? ? ? // Write out any hidden serialization magic
? ? ? ? s.defaultWriteObject();

? ? ? ? // Write out HashMap capacity and load factor
? ? ? ? s.writeInt(map.capacity());
? ? ? ? s.writeFloat(map.loadFactor());

? ? ? ? // Write out size
? ? ? ? s.writeInt(map.size());

? ? ? ? // Write out all elements in the proper order.
? ? ? ? for (Iterator i=map.keySet().iterator(); i.hasNext(); )
? ? ? ? ? ? s.writeObject(i.next());
? ? }


? ? // java.io.Serializable的讀取函數(shù)
? ? // 將HashSet的“總的容量,加載因子,實(shí)際容量,所有的元素”依次讀出
? ? private void readObject(java.io.ObjectInputStream s)
? ? ? ? throws java.io.IOException, ClassNotFoundException {
? ? ? ? // Read in any hidden serialization magic
? ? ? ? s.defaultReadObject();

? ? ? ? // Read in HashMap capacity and load factor and create backing HashMap
? ? ? ? int capacity = s.readInt();
? ? ? ? float loadFactor = s.readFloat();
? ? ? ? map = (((HashSet)this) instanceof LinkedHashSet ?
? ? ? ? ? ? ? ?new LinkedHashMap<E,Object>(capacity, loadFactor) :
? ? ? ? ? ? ? ?new HashMap<E,Object>(capacity, loadFactor));

? ? ? ? // Read in size
? ? ? ? int size = s.readInt();

? ? ? ? // Read in all elements in the proper order.
? ? ? ? for (int i=0; i<size; i++) {
? ? ? ? ? ? E e = (E) s.readObject();
? ? ? ? ? ? map.put(e, PRESENT);
? ? ? ? }
? ? }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
從上述HashSet源代碼可以看出,它其實(shí)就是一個(gè)對(duì)HashMap的封裝而已。所有放入HashSet中的集合元素實(shí)際上由HashMap的key來(lái)保存,而HashMap的value則存儲(chǔ)了一個(gè)PRESENT,它是一個(gè)靜態(tài)的Object對(duì)象。

HashSet的絕大部分方法都是通過(guò)調(diào)用HashMap的方法來(lái)實(shí)現(xiàn)的,因此HashSet和HashMap兩個(gè)集合在實(shí)現(xiàn)本質(zhì)上是相同的。

根據(jù)HashMap的一個(gè)特性: 將一個(gè)key-value對(duì)放入HashMap中時(shí),首先根據(jù)key的hashCode()返回值決定該Entry的存儲(chǔ)位置,如果兩個(gè)key的hash值相同,那么它們的存儲(chǔ)位置相同。如果這個(gè)兩個(gè)key的equalus比較返回true。那么新添加的Entry的value會(huì)覆蓋原來(lái)的Entry的value,key不會(huì)覆蓋。因此,如果向HashSet中添加一個(gè)已經(jīng)存在的元素,新添加的集合元素不會(huì)覆蓋原來(lái)已有的集合元素。

現(xiàn)在我們通過(guò)一個(gè)實(shí)際的例子來(lái)看看是否真正理解了HashMap和HashSet存儲(chǔ)元素的細(xì)節(jié):

class Name
{
? ? private String first;
? ? private String last;
? ? public Name(String first, String last)?
? ? {
? ? ? ? this.first = first;
? ? ? ? this.last = last;
? ? }
? ? public boolean equals(Object o)?
? ? {
? ? ? ? if (this == o)
? ? ? ? {
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? if (o.getClass() == Name.class)
? ? ? ? {
? ? ? ? ? ? Name n = (Name)o;
? ? ? ? ? ? return n.first.equals(first)
? ? ? ? ? ? ? ? && n.last.equals(last);
? ? ? ? }
? ? ? ? return false;
? ? }
}
public class HashSetTest
{
? ? public static void main(String[] args)?
? ? {
? ? ? ? Set<Name> s = new HashSet<Name>();
? ? ? ? s.add(new Name("abc", "123"));
? ? ? ? System.out.println(
? ? ? ? ? ? s.contains(new Name("abc", "123")));
? ? }?
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
上面程序中向HashSet里添加了一個(gè)new Name(“abc”,”123”)對(duì)象之后,立即通過(guò)程序判斷該HashSet里是否包含一個(gè)new Name(“abc”,”123”)對(duì)象。粗看上去,很容易以為該程序會(huì)輸出true。

實(shí)際上會(huì)輸出false。因?yàn)镠ashSet判斷兩個(gè)對(duì)象相等的標(biāo)準(zhǔn)是想通過(guò)hashCode()方法計(jì)算出其hash值,當(dāng)hash值相同的時(shí)候才繼續(xù)判斷equals()方法。而如上程序我們并沒(méi)有重寫(xiě)hashCode()方法。所以兩個(gè)Name類的hash值并不相同,因此HashSet會(huì)把其當(dāng)成兩個(gè)對(duì)象來(lái)處理。

所以,當(dāng)我們要將一個(gè)類作為HashMap的key或者存儲(chǔ)在HashSet的時(shí)候。通過(guò)重寫(xiě)hashCode()和equals(Object object)方法很重要,并且保證這兩個(gè)方法的返回值一致。當(dāng)兩個(gè)類的hashCode()返回一致時(shí),應(yīng)該保證equasl()方法也返回true。當(dāng)給上述Name類增加如下方法:

public void hashCode(){

return first.hashCode()+last.hashCode();
}
1
2
3
4
此時(shí)我們測(cè)試的方法會(huì)返回true。
---------------------?
作者:不能說(shuō)的秘密go?
來(lái)源:CSDN?
原文:https://blog.csdn.net/canot/article/details/51240251?
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!

總結(jié)

以上是生活随笔為你收集整理的HashSet源码解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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