重写java.util.HashSets contains方法会导致哪些问题?

问题描述

我想使用HashSet存储一些对象:

public class Storedobject{
    Type type; //Type is an enum
    //other fields

    Type getType(){return type;}
}

现在,我只想存储同一Storedobject中的一个Type,所以我在contains()的子类中覆盖HashSet

public MySet<E extends Storedobject> extends java.util.HashSet<E>{
    @Override
    public boolean contains(Object o) {
        if(Storedobject.class.isAssignableFrom(o.getClass())) {//if o implements Storedobject
            for(Storedobject s : this) {
                if(s.getType() == ((Storedobject) o).getType()) return true;
            }
        }
        return false
    }
}

在此之前,我想使用HashSet修改equals()中的Storedobject。但是,以上方法似乎是一种更短且更安全的方法,尤其是在我的情况下,所有存储的对象都实现了一个接口,并且没有扩展同一类。

现在我的问题是:这种实施安全吗?我试图搜索可能会损坏的东西,但没有找到任何东西。我了解到,覆盖equals()可能会破坏集合。 而且,由于此子类未将HashSet用于HashMap,因此是否违反了contains()的目的?

解决方法

HashMap<Type,StoredObject>是适合此的集合。

如果您覆盖equals(Object),那么您还必须覆盖hashCode(使其实现Comparable并可能覆盖toString也不是坏主意)。使用@Override批注以确保您拥有正确的参数类型和拼写-非常容易出错和调试混乱。

出什么问题了?

  • HashSet中有很多方法可以覆盖,因此需要大量工作。
  • 将来的Java版本中可能会向HashSet添加更多方法-您将如何注意这一点?
  • contains应该是O(1)操作(假设哈希码分布良好),但是OP实现是O(n)。
  • 在另一个Set.equals上的
  • Set将报告错误的结果。

还要注意,StoredObject.class.isAssignableFrom(o.getClass())最好写成o instanceof StoredObject(假设您正确地使用了isAssignableFrom)。

,

此实现安全吗?

绝对不是。 HashSet上还有其他无法正常使用的方法,例如add(),设置的大小不正确。

此外,该实现将完全破坏contains方法的性能,使其在 O(n)中运行,而不是在 O(1)中运行。

如果您需要Set的相等性定义不同于equals()hashCode()实现的对象自然定义,请使用TreeSet并提供自定义Comparator

class MySet<E extends StoredObject> extends java.util.TreeSet<E> {
    public MySet() {
        super(Comparator.comparing(StoredObject::getType));
    }
}

我确实同意Tom Hawtin - tackline,认为HashMap<Type,StoredObject>是更好的选择,因为它可以让您为给定的StoredObject获取Type,否则很难与Set有关。它还可以让您仅凭Type来检查是否存在,而不必为检查创建虚拟StoredObject对象。