问题描述
我想使用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
将报告错误的结果。
Set.equals
上的还要注意,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
对象。