簡體   English   中英

Set 如何檢查重復項? Java 哈希集

[英]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, tt1是兩個不同的數組,因此它們的哈希碼不同。 同樣,因為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;
}

如果你想放置不同的數組,你必須嘗試使用TreeSetComparator的正確實現,或者包裝你的數組或使用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());

至於SetMap鍵中使用的對象的可變性:

  • boolean equals(Object)使用的字段不應該被靜音,因為被靜音的對象可能等於另一個。 Set 將不再包含不同的值。
  • 對於基於散列的集合( HashSetHashMap ), 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM