[英]How to fix HashSet that allows duplicates?
I'm using a java.util.Set interface with a java.util.HashSet implementation and storing it in a Map. 我正在将java.util.Set接口与java.util.HashSet实现一起使用,并将其存储在Map中。
I add an object to a Set then retrieve the Set object again and am able to add another object that is equal to the first. 我将一个对象添加到Set中,然后再次检索Set对象,并能够添加与第一个对象相等的另一个对象。
When adding the seemingly equal objects, Set.add returns true
and two equal objects are stored in a HashSet. 当添加看似相等的对象时, Set.add返回
true
并且两个相等的对象存储在HashSet中。 How is this possible and what can I do to fix this apparent breakage of the Set contract? 这怎么可能?我该如何解决Set合同的这种明显的损坏?
I'm using Java 12 via IntelliJ IDEA 2018.3.6 and have checked my java.lang.Object.hashCode implementation for the class of the two objects I add to the Set, with both returning the same hash code. 我正在通过IntelliJ IDEA 2018.3.6使用Java 12,并检查了我在java.lang.Object.hashCode实现中添加到Set中的两个对象的类,两个对象均返回相同的哈希码。 I've also checked the java.lang.Objects.equals implementation and it returns
true
when the method is used to check their equality. 我还检查了java.lang.Objects.equals实现,当使用该方法检查其相等性时它返回
true
。 Both objects are wrapped in another object, Entity, but that only forwards the objects' hashCode
and equals
implementations. 两个对象都包装在另一个对象Entity中,但这仅转发对象的
hashCode
并equals
实现。
class Model {
...
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Offer)) {
return false;
}
Offer offer = (Offer) obj;
return Objects.equals(id, offer.id)
&& Objects.equals(name, offer.name)
;
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + Objects.hashCode(id);
result = 31 * result + Objects.hashCode(name);
return result;
}
...
}
class Store {
...
private static class Entry {
Object value;
Entry(Object value) {
this.value = value;
}
Object getValue() {
return value;
}
@Override
public boolean equals(Object obj) {
return Objects.equals(value, obj);
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public String toString() {
return "Entry[value = " + value + "]";
}
}
...
private Map<Class<?>, Set<Entry>> data;
...
private Set<Entry> get(Class<?> type) {
return data.getOrDefault(type, new HashSet<>());
}
@Override
public void persist(Object obj) {
Entry entry = new Entry(obj);
Set<Entry> objects = get(obj.getClass());
if (objects == null) {
objects = new HashSet<>();
}
if (!objects.add(entry)) {
throw new ObjectExistsException
("Object already exists: " + obj);
}
data.put(obj.getClass(), objects);
}
...
}
When obj1
and obj2
of type Model are equal and objects
already contains obj1
wrapped in an Entry object, I expect obj2
not to be added to objects
when obj2
is wrapped in entry
and for objects.add(entry)
to return false
then an ObjectExistsException to be thrown. 当Model类型的
obj1
和obj2
相等并且objects
已经包含包裹在Entry对象中的obj1
,我希望当obj2
包裹在entry
并且object.add objects.add(entry)
返回false
时,不将obj2
添加到objects
,然后ObjectExistsException被抛出。
However, what actually is happening is objects.add(entry)
returns true
and obj2
wrapped in entity
is being added to objects
. 然而,什么是真正的情况是
objects.add(entry)
返回true
和obj2
包裹在entity
被添加到objects
。
@Override
public boolean equals(Object obj) {
return Objects.equals(value, obj);
}
This isn't a correct implementation of Entry.equals
. 这不是
Entry.equals
的正确实现。 This potentially compares an Entry
with the value held by the current entry. 这可能会将一个
Entry
与当前条目所保存的值进行比较。 (Like comparing a letter with an envelope). (就像比较一封信和一个信封)。
Make your equals method check that obj
is an Entry
, and get its value, and check equality to that. 使您的equals方法检查
obj
是否为Entry
,并获取其值,然后检查是否相等。
You were right @Andy Turner, but it was @Andreas that pointed me in the right direction. 你是正确的@安迪·特纳(Andy Turner),但正是@安德里亚斯(Andreas)向我指出了正确的方向。 I thought I would be lazy and not write a full
equals
method implementation, but it cost me. 我以为我会很懒,不写一个完整的
equals
方法实现,但是却花了我很多时间。 It should have been like this: 应该是这样的:
private static class Entry {
...
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Entry)) {
return false;
}
Entry entry = (Entry) obj;
return Objects.equals(getValue(), entry.getValue());
}
...
}
Thank you both. 谢谢你俩。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.