hash 值重复_面试题:HashSet是如何保证元素不重复的
面試官:你能簡單介紹List和Set有什么區別嗎?
小憨:
- List是一個有序的集合,在內存是連續存儲的,可以存儲重復的元素,List查詢快,增刪慢;
- Set是一個無序的集合,在內存中不連續,不可以存儲重復的元素,Set增刪快,查詢慢;
面試官:那HashSet是如何保證元素不重復的?
小憨:3分鐘。。。
為了避免出現小憨這種知其然不知其所以然的尷尬,我們還是有必要來分析下上述問題的。
客官,且看下文
我們都知道HashSet存放的元素是不允許重復的,那么HashSet又是是如何保證元素不可重復的,你知道嗎?
先看段源碼
public class HashSetextends AbstractSet
implements Set, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public HashSet(Collection extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
}
乍一看這段代碼,哎呦我去,new HashSet()操作不就不是維護了一個HashMap嘛,要是這么往下演的話,我覺得我這點功力也能看個大概呀!
諸位同仁,咱接著往下看
public boolean add(E e) {return map.put(e, PRESENT)==null;
}
什么,這不就是map操作么,瞬間我來個下飯推理;
Map中的key是不允許重復的,而你HashSet正好利用我Map中key不重復的特性來校驗重復元素,妙哉妙哉。
確實,HashSet確實是利用Map的這一特性實現了元素的不重復特性,但是我們再來深挖一下,Map他又是如何來保證key不重復的呢?
與其說這篇文章是介紹HashSet如何保證元素不重復的,倒不如說Map是如何保證Key不重復的。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node[] tab; Node p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// 1、如果該位置不存在,直接插入
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node e; K k;
// 2、如果存在,判斷是否是重復元素
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
上面部分我重點圈了兩段代碼,分別是1和2。
第一段
if ((p = tab[i = (n - 1) & hash]) == null)這段代碼其實主要是通過hash計算該元素的位置,然后判斷該位置是否有值,如果沒有值,那么可以直接插入,最后返回null;
第二段
if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
e = p;
如果通過計算,該位置上已經有其他元素,那么接下來就會通過hash和equals進行判斷,判斷它是不是重復元素,如果重復元素,那么最后會將這個重復元素返回。
通過第二段代碼我們可以發現,判斷元素是否重復,使用的是hash和equals方法進行判斷的,所有我們Set里面如果存放的是對象,那么一定要重寫hash和equals方法。
現在是不是很清晰了,為啥要重寫equals方法了,不會出現那么詭異的代碼了,這兩個對象值都一樣啊,為什么Set沒去重呢!
總結
以上是生活随笔為你收集整理的hash 值重复_面试题:HashSet是如何保证元素不重复的的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windwos cakephp mysq
- 下一篇: python通过跳板机连接服务器_使用p