简体   繁体   English

如何修复允许重复的HashSet?

[英]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中,但这仅转发对象的hashCodeequals实现。

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类型的obj1obj2相等并且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)返回trueobj2包裹在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM