Set 接口基本介紹:
注意:取出的順序的順序雖然不是添加的順序,但是他的固定
set接口的常用方法:
和 List 接口一樣, Set 接口也是 Collection 的子接口,因此,常用方法和 Collection 接口一樣.
set的遍歷方式:
HashSet的全面說明:
HashSet的暢通方法解析:
1. 在執行 add 方法后,會返回一個 boolean 值
2. 如果添加成功,返回 true, 否則返回 false
3. 可以通過 remove 指定刪除哪個對象? ?如:
HashSet set = new HashSet();
set.remove("john");
HashSet的源碼分析:
代碼:
HashSet hashSet = new HashSet();
hashSet.add("java");//到此位置,第 1 次 add 分析完畢.
hashSet.add("php");//到此位置,第 2 次 add 分析完畢
hashSet.add("java");
System.out.println("set=" + hashSet);
源碼分析:
1. 構造器走的源碼
public HashSet() {
map = new HashMap<>();
}
2. 執行 add()
public boolean add(E e) {//e = "java"
return map.put(e, PRESENT)==null;//(static) PRESENT = new Object();
}
3.執行 put() , 該方法會執行 hash(key) 得到 key 對應的 hash 值 算法 h = key.hashCode()) ^ (h >>> 16)
public V put(K key, V value) {//key = "java" ,value = PRESENT 共享
return putVal(hash(key), key, value, false, true);
}
4.執行 putVal
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i; //定義了輔助變量
//table 就是 HashMap 的一個數組,類型是 Node[]
//if 語句表示如果當前 table 是 null, 或者 大小=0
//就是第一次擴容,到 16 個空間.
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//(1)根據 key,得到 hash 去計算該 key 應該存放到 table 表的哪個索引位置
//并把這個位置的對象,賦給 p
//(2)判斷 p 是否為 null
//(2.1) 如果 p 為 null, 表示還沒有存放元素, 就創建一個 Node (key="java",value=PRESENT)
//(2.2) 就放在該位置 tab[i] = newNode(hash, key, value, null)
if ((p = tab[i = (n - 1) & hash]) == null)
ab[i] = newNode(hash, key, value, null);
else {
//一個開發技巧提示: 在需要局部變量(輔助變量)時候,在創建
Node<K,V> e; K k; //
//如果當前索引位置對應的鏈表的第一個元素和準備添加的 key 的 hash 值一樣
//并且滿足 下面兩個條件之一:
//(1) 準備加入的 key 和 p 指向的 Node 結點的 key 是同一個對象
//(2) p 指向的 Node 結點的 key 的 equals() 和準備加入的 key 比較后相同就不能加入
? ? ? ? ? ? ? if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//再判斷 p 是不是一顆紅黑樹,
//如果是一顆紅黑樹,就調用 putTreeVal , 來進行添加
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//如果 table 對應索引位置,已經是一個鏈表, 就使用 for 循環比較
//(1) 依次和該鏈表的每一個元素比較后,都不相同, 則加入到該鏈表的最后
// 注意在把元素添加到鏈表后,立即判斷 該鏈表是否已經達到 8 個結點
// , 就調用 treeifyBin() 對當前這個鏈表進行樹化(轉成紅黑樹)
// 注意,在轉成紅黑樹時,要進行判斷, 判斷條件
// if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
// resize();
// 如果上面條件成立,先 table 擴容.
// 只有上面條件不成立時,才進行轉成紅黑樹
//(2) 依次和該鏈表的每一個元素比較過程中,如果有相同情況,就直接 break
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD(8) - 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;
//size 就是我們每加入一個結點 Node(k,v,h,next), size++
if (++size > threshold)
resize();//擴容
afterNodeInsertion(evict);
return null;
}
*/
}
}
當我們向 hashset 增加一個元素,-> Node -> 加入 table , 就算是增加了一個 size++