[英]How Set checks for duplicates? Java HashSet
對於下面的代碼,它輸出“ 1 ”。 和第二個代碼輸出“2”我不明白為什么會這樣。 是因為我添加了相同的對象嗎? 我應該如何實現所需的輸出 2。
import java.util.*;
public class maptest {
public static void main(String[] args) {
Set<Integer[]> set = new HashSet<Integer[]>();
Integer[] t = new Integer[2];
t[0] = t[1] = 1;
set.add(t);
Integer[] t1 = new Integer[2];
t[0] = t[1] = 0;
set.add(t);
System.out.println(set.size());
}
}
第二個代碼:
import java.util.*;
public class maptest {
public static void main(String[] args) {
Set<Integer[]> set = new HashSet<Integer[]>();
Integer[] t = new Integer[2];
t[0] = t[1] = 1;
set.add(t);
Integer[] t1 = new Integer[2];
t1[0] = t1[1] = 1;
set.add(t1);
System.out.println(set.size());
}
}
Set
實現可能調用t.hashCode()
並且由於數組不會覆蓋Object.hashCode
方法,因此相同的對象將具有相同的哈希碼。 因此,更改數組的內容不會影響其哈希碼。 要正確獲取數組的哈希碼,您應該調用Arrays.hashCode
。
無論如何,您不應該真正將可變內容放入集合中,因此我建議您將不可變列表放入集合中。 如果您想堅持使用數組,只需創建一個新數組,就像您對t1
所做的那樣,並將其放入集合中。
編輯:
對於代碼 2, t
和t1
是兩個不同的數組,因此它們的哈希碼不同。 同樣,因為hashCode
方法沒有在數組中被覆蓋。 數組的內容不會影響哈希碼,無論它們是否相同。
您正在將Object
添加到一個Set
中
不包含重復元素。
您只將一個Object
添加到Set
。 您只需更改其內容的值。 要了解我的意思,請嘗試添加System.out.println(set.add(t));
.
作為add()
方法:
如果此集合尚未包含指定的元素,則返回 true
此外,您的t1
與您的第一個代碼片段完全無關,因為您從未使用過它。
在您的第二個代碼片段中,它輸出兩個,因為您要向Set
添加兩個不同的Integer[]
Objects
嘗試打印出Objects
的哈希碼以查看其工作原理:
Integer[] t = new Integer[2];
t[0] = t[1] = 1;
//Before we change the values
System.out.println(t.hashCode());
Integer[] t1 = new Integer[2];
t1[0] = t1[1] = 1;
//After we change the values of t
System.out.println(t.hashCode());
//Hashcode of the second object
System.out.println(t1.hashCode());
輸出:
//Hashcode for t is the same before and after modifying data
366712642
366712642
//Hashcode for t1 is different from t; different object
1829164700
一個Set
只包含不同的元素(這是它的本質)。 基本實現HashSet
使用hashCode()
首先查找包含值的存儲桶,然后使用equals(Object)
查找不同的值。
數組很簡單:它們的 hashCode() 使用默認值,從Object
繼承,因此依賴於引用。 equals(Object)
也與Object
相同:它只檢查標識,即:引用必須是相等的。
定義為 Java:
public boolean equals(Object other) {
return other == this;
}
如果你想放置不同的數組,你必須嘗試使用TreeSet
和Comparator
的正確實現,或者包裝你的數組或使用List
或另一個Set
:
Set<List<Integer[]>> set = new HashSet<>();
Integer[] t = new Integer[]{1, 1};
set.add(Arrays.asList(t));
Integer[] t1 = new Integer[]{1, 1};
set.add(Arrays.asList(t1));
System.out.println(set.size());
至於Set
或Map
鍵中使用的對象的可變性:
boolean equals(Object)
使用的字段不應該被靜音,因為被靜音的對象可能等於另一個。 Set 將不再包含不同的值。HashSet
, HashMap
), int hashCode()
使用的字段不應該被靜音,因為如上所述,它們通過將項目放入桶中進行操作。 如果 hashCode() 更改,則對象在存儲桶中的位置很可能也會更改: Set
將包含兩次相同的引用。int compareTo(T)
或Comparator::compare(T,T)
使用的字段不應出於與equals
相同的原因而靜音: SortedSet
不會知道發生了變化。如果需要,您必須首先從集合中刪除項目,然后對其進行變異,然后重新添加它。
java.util.Set
實現如何檢查重復對象取決於實現,但根據Set
的文檔,“重復”的適當含義是o1.equals(o2)
。
由於HashSet
特別是基於哈希表,因此它將通過計算呈現給它的對象的hashCode()
來查找重復項,然后遍歷相應哈希桶中的所有對象(如果有)。
數組不會覆蓋hashCode()
或equals()
,因此它們實現實例標識,而不是值標識。 因此,無論其元素的值如何,給定的數組始終具有相同的哈希碼,並且始終equals()
本身且僅equals()
自身。 您的第一個代碼將相同的數組對象添加到集合中兩次。 不管其元素的值如何,它仍然是同一個集合。 第二個代碼將兩個不同的數組對象添加到一個集合中。 無論其元素的值如何,它們都是不同的對象。
還要注意,如果您有實現值標識的可變對象,這樣它們的相等性和哈希碼取決於其成員的值,那么在它是Set
的成員時修改這樣的對象很可能會破壞Set
。 這是在每個實現的基礎上記錄的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.