哪些问题可能源于覆盖 java.util.HashSets contains() 方法?
Which problems can stem from overriding java.util.HashSets contains()-method?
我想用一个HashSet
来存储一些对象:
public class StoredObject{
Type type; //Type is an enum
//other fields
Type getType(){return type;}
}
现在,我只想存储相同 Type
中的一个 StoredObject
,因此我在 HashSet
的子 class 中覆盖了 contains()
:
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
修改StoredObject
的equals()
。但是,上面的方法似乎是一种更短且更安全的方法,尤其是在我的情况下,存储的对象都实现了一个接口并且不扩展相同的 class.
现在我的问题是:这个实现安全吗?我试图寻找它可以破坏的东西,但没有找到任何东西。我读到覆盖 equals()
会破坏集合。
另外,这个 subclass 是否违背了 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
是正确的)。
Is this implementation safe?
绝对不是。 HashSet
上还有其他方法无法正常工作,例如add()
,导致集合的大小不正确。
此外,该实现会完全破坏 contains
方法的性能,使其在 O(n) 而不是 [=35] 中变为 运行 =]O(1).
如果您需要 Set
的等式定义不同于 equals()
和 hashCode()
实现的对象自然定义,请使用 TreeSet
并提供自定义 Comparator
.
class MySet<E extends StoredObject> extends java.util.TreeSet<E> {
public MySet() {
super(Comparator.comparing(StoredObject::getType));
}
}
我同意 ,HashMap<Type, StoredObject>
是一个更好的选择,因为它允许您为给定的 Type
获得 StoredObject
,否则Set
很难做到。它还允许您检查仅给定 Type
是否存在,而无需为检查创建虚拟 StoredObject
对象。
我想用一个HashSet
来存储一些对象:
public class StoredObject{
Type type; //Type is an enum
//other fields
Type getType(){return type;}
}
现在,我只想存储相同 Type
中的一个 StoredObject
,因此我在 HashSet
的子 class 中覆盖了 contains()
:
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
修改StoredObject
的equals()
。但是,上面的方法似乎是一种更短且更安全的方法,尤其是在我的情况下,存储的对象都实现了一个接口并且不扩展相同的 class.
现在我的问题是:这个实现安全吗?我试图寻找它可以破坏的东西,但没有找到任何东西。我读到覆盖 equals()
会破坏集合。
另外,这个 subclass 是否违背了 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
是正确的)。
Is this implementation safe?
绝对不是。 HashSet
上还有其他方法无法正常工作,例如add()
,导致集合的大小不正确。
此外,该实现会完全破坏 contains
方法的性能,使其在 O(n) 而不是 [=35] 中变为 运行 =]O(1).
如果您需要 Set
的等式定义不同于 equals()
和 hashCode()
实现的对象自然定义,请使用 TreeSet
并提供自定义 Comparator
.
class MySet<E extends StoredObject> extends java.util.TreeSet<E> {
public MySet() {
super(Comparator.comparing(StoredObject::getType));
}
}
我同意 HashMap<Type, StoredObject>
是一个更好的选择,因为它允许您为给定的 Type
获得 StoredObject
,否则Set
很难做到。它还允许您检查仅给定 Type
是否存在,而无需为检查创建虚拟 StoredObject
对象。