一、自定義一個Student類
package date0504;
public class Student {
private String id;
Student(String id){
this.id=id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
二、使用HashSet中的add()方法將上述對象存入
HashSet hashset = new HashSet<>();
//add()方法的返回值為boolean類型。若為true,存入成功。否則,數據存入失敗。
System.out.println(hashset.add(new Student("1"))); //存入第一個對象
System.out.println(hashset.add(new Student("2"))); //存入第二個對象
System.out.println(hashset.add(new Student("1"))); //存入第三個對象
輸出結果如下
true
true
true
三、從上述結果看出,三個對象都添加成功了。我們來分析一下add()方法的具體過程。
1、HashSet hashset = new HashSet<>(); 第一行代碼調用了HashSet的無參構造方法,我們來看一下它的源碼。
public HashSet() {
map = new HashMap<>(); //調用了HashMap無參構造方法,創建了map對象。
}
2、我們主要分析第三行代碼——hashset.add(new Student("1");
//HashMap中的add方法
public boolean add(E e) {
return map.put(e, PRESENT)==null; //調用了HashMap中的put方法
}
3、再來看這行代碼———map.put(e, PRESENT)==null;
//HashMap中的put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true); //調用本類中的putVal方法和hash方法。
}
4、讓我們來看一下HashMap中的hash方法
static final int hash(Object key) {
int h;
// 若傳入對象不為null,則返回此對象調用hashCode方法(其實是Object類中的hashCode方法)的返回值
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
5、分析HashMap中的putVal方法。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node[] tab; Node p; int n, i;
//1、第一次添加對象時,tab為null,執行其后語句。
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //2、如果存儲元素的table為空,則進行必要字段的初始化,n為初始化后數組長度。
//3、第一次傳入數據時,p=null,執行if語句下面的代碼。第二次添加數據時的n和第一次添加后的n一樣,但是不同傳入對象的hash值是不一樣的,所以i不同,p=null。故每次傳入不同對象p都為null,不會執行else語句塊。
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
//4、如果兩個對象的hash值一樣,p就不為null,就執行else語句塊。
else {
Node e; K k;
//5、當前元素和上一個元素的hash值相等
if (p.hash == hash &&
//6、當前對象的地址和之前對象的地址不同,也不為null。並且這里調用了當前傳入對象的equals方法。
((k = p.key) == key || (key != null && key.equals(k))))
//7、如果if語句為true,把之前的對象賦值給e。
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;
}
}
//8、e不為null,故返回 oldValue。
//9、HashMap中的put方法也返回oldValue。
//10、oldValue不為null,故add方法返回false。
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}鄭州 不 孕 不 育 醫 院:http://jbk.39.net/yiyuanzaixian/zztjyy/
//11、若執行下面代碼,則返回null,add方法返回值為true。添加不同的對象都會執行下面代碼。
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
四、那我們分析完add方法的執行過程后,該如何實現不能重復添加id相同的Student對象這個目的呢?
1、在Student類中重寫hashCode方法和equals方法
public class Student {
private String id;
Student(String id){
this.id=id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public boolean equals(Object o) {
Student stu =(Student)o; //向下轉型
return id.equals(stu.id); //id為String類型,故只要兩個字符串內容相同,返回true。
}
public int hashCode() {
return id.hashCode(); //調用了String類中的hashCode方法,如果兩個字符串相同,那hashCode方法返回true。
}
}
2、使用HashSet存入對象。
HashSet hashset = new HashSet<>();
//add()方法的返回值為boolean類型。若為true,存入成功。否則,數據存入失敗。
System.out.println(hashset.add(new Student("1"))); //存入第一個對象
System.out.println(hashset.add(new Student("2"))); //存入第二個對象
System.out.println(hashset.add(new Student("1"))); //存入第三個對象
輸出結果如下
true
true
false
3、可以看出:當重寫了hashCode方法和equals方法后,就實現了此目的。
五、注意事項
1、當泛型為Object的時候傳入其他的非Student類的時候有可能會出現異常。
HashSet