java hashmap 去重复_为什么我在Java HashMap中得到重复的键?
本問題已經有最佳答案,請猛點這里訪問。
我似乎在標準Java HashMap中得到重復的鍵。"重復"是指鍵的equals()方法相等。這是有問題的代碼:
import java.util.Map;
import java.util.HashMap;
public class User {
private String userId;
public User(String userId) {
this.userId = userId;
}
public boolean equals(User other) {
return userId.equals(other.getUserId());
}
public int hashCode() {
return userId.hashCode();
}
public String toString() {
return userId;
}
public static void main(String[] args) {
User arvo1 = new User("Arvo-Part");
User arvo2 = new User("Arvo-Part");
Map map = new HashMap();
map.put(arvo1,1);
map.put(arvo2,2);
System.out.println("arvo1.equals(arvo2):" + arvo1.equals(arvo2));
System.out.println("map:" + map.toString());
System.out.println("arvo1 hash:" + arvo1.hashCode());
System.out.println("arvo2 hash:" + arvo2.hashCode());
System.out.println("map.get(arvo1):" + map.get(arvo1));
System.out.println("map.get(arvo2):" + map.get(arvo2));
System.out.println("map.get(arvo2):" + map.get(arvo2));
System.out.println("map.get(arvo1):" + map.get(arvo1));
}
}
這是結果輸出:
arvo1.equals(arvo2): true
map: {Arvo-Part=1, Arvo-Part=2}
arvo1 hash: 164585782
arvo2 hash: 164585782
map.get(arvo1): 1
map.get(arvo2): 2
map.get(arvo2): 2
map.get(arvo1): 1
如您所見,
兩個User對象上的equals()方法將返回true,并且它們的哈希碼相同,但是它們各自在map中形成了不同的key。此外,map繼續區分最近四個get()調用中的兩個User鍵。
這直接與文檔相矛盾:
More formally, if this map contains a mapping from a key k to a value v such that (key==null ? k==null : key.equals(k)), then this method returns v; otherwise it returns null. (There can be at most one such mapping.)
這是一個錯誤嗎?我在這里想念什么嗎?我正在運行通過Homebrew安裝的Java版本1.8.0_92。
編輯:此問題已被標記為該另一個問題的重復,但是我將保留該問題,因為它標識出與equals()似乎不一致的地方,而另一個問題假定錯誤在于hashCode() 。希望此問題的存在將使此問題更易于搜索。
嘗試將@Override添加到您的equals和hashCode方法中(始終是最佳實踐),看看是否獲得任何有用的信息。
為了將來出現這種錯別字或錯誤,請始終讓您的IDE為您生成方法。然后調整它們,使其看起來像您想要的。這將創建帶有@Override批注的正確方法。
問題出在您的equals()方法中。 Object.equals()的簽名是equals(Object),但是在您的情況下,它是equals(USER),因此這是兩個完全不同的方法,并且hashmap使用Object參數調用一個方法。您可以通過在等號上放置一個@Override注釋來進行驗證-它將生成編譯器錯誤。
equals方法應為:
@Override
public boolean equals(Object other) {
if(other instanceof User){
User user = (User) other;
return userId.equals(user.userId);
}
return false;
}
作為最佳實踐,您應始終將@Override放在要覆蓋的方法上-這樣可以省去很多麻煩。
對我來說有趣的問題是:動態鍵入是否應該與呼叫最匹配?可以將Object o = new User(); -> o.equals(o)調度為與運行時類型最佳匹配。 -但是Java使用靜態編譯時類型查找正確的重載,而不是動態運行時參數-為了完整性,您可以在答案中包括該內容!
它將被調度為最佳的編譯時間類型。 Java沒有動態類型
嗯,您缺少hasmap被編譯為始終調用equals(object)的功能,因此,如果您擁有equals(user),它將永遠不會調用它。但是,如果您在代碼中執行User user = new User(); user.equals(new User()),則會調用equals(user)。但是,如果按照示例Object user = new User(); user.equals(new User())的方式進行操作,則會調用equals(object)。我希望能清除它
清除了!我認為將其添加到其他用戶的答案中可能會有所幫助:-)
除非明確保證派生類不會成為問題,否則我將避免在等于中使用instanceof。
您的equals方法不會覆蓋equals,并且Map中的類型在運行時會被刪除,因此實際的equals方法為equals(Object)。您的等值線應該看起來像這樣:
@Override
public boolean equals(Object other) {
if (!(other instanceof User))
return false;
User u = (User)other;
return userId.equals(u.userId);
}
就像其他人回答的那樣,您對equals方法簽名有疑問。根據Java equals最佳實踐,您應實現以下所示的equals:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return userId.equals(user.userId);
}
hashCode()方法同樣適用。請參見覆蓋Java
中的equals()和hashCode()方法
The Second Problem
您現在沒有重復項,但是您有一個新問題,您的hashMap僅包含一個元素:
map: {Arvo-Part=2}
這是因為兩個User對象都引用相同的String(JVM String Interning),并且從hashMapangular來看,您的兩個對象是相同的,因為這兩個對象在哈希碼中相等,并且均等于方法。因此,當您將第二個對象添加到hashMap時,您將覆蓋第一個對象。
為避免此問題,請確保為每個用戶
使用唯一的ID
A simple demonstration on your users :
好的,所以首先,代碼不會編譯。缺少此方法:
other.getUserId()
但是除此之外,您還需要@Override equals方法,像Eclipse這樣的IDE也可以幫助生成equals和hashCode btw。
@Override
public boolean equals(Object obj)
{
if(this == obj)
return true;
if(obj == null)
return false;
if(getClass() != obj.getClass())
return false;
User other = (User) obj;
if(userId == null)
{
if(other.userId != null)
return false;
}
else if(!userId.equals(other.userId))
return false;
return true;
}
如Chrylis所建議,通過將@Override添加到hashCode和equals中,您將得到編譯錯誤,因為equals方法的簽名是public boolean equals(Object other),因此您實際上并沒有覆蓋默認值。 (來自Object類)equals方法。這導致兩個用戶最終都位于hashMap內的同一存儲桶中(hashCode被覆蓋,并且兩個用戶具有相同的哈希碼),但是當檢查是否相等時,它們將不同,因為使用了默認的equals方法,這意味著比較內存地址。
用以下內容替換equals方法以獲得預期的結果:
@Override
public boolean equals(Object other) {
return getUserId().equals(((User)other).getUserId());
}
equals不應拋出(如果類型不匹配,您的版本將拋出)。
總結
以上是生活随笔為你收集整理的java hashmap 去重复_为什么我在Java HashMap中得到重复的键?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java安装了为什么运行不了_安装jdk
- 下一篇: java类二次加载_深入理解java之类