我很好奇,是否有Set
只需要.equals()
来确定唯一性?
查看Set
中的java.util
个类时,我只能找到需要HashSet
和.hashCode()
的{{1}}(或通常为TreeSet
)这需要SortedSet
。我找不到任何只使用Comparator
的课程。
如果我有.equals()
方法,用它来确定对象唯一性就足够了吗?因此,.equals()
实现只需要使用Set
?或者我错过了.equals()
不足以确定.equals()
实施中对象唯一性的内容?
请注意,我知道Java实践,如果我们覆盖Set
,我们应该覆盖.equals()
以维护.hashCode()
中定义的合同。
答案 0 :(得分:4)
.equals
方法本身就足以正确实现集合,但不能有效地实现 。
哈希码或比较器的要点是它们提供了在一些有序结构(哈希表或二叉树)中排列对象的方法,以便快速确定对象是否是出现在集合中。如果您只有.equals
方法来比较对象对,则无法以任何有意义的顺序排列集合的内容;你只有一堆松散的对象,因此,例如,确认对象是唯一的,需要将每个添加的对象与混乱中的每个其他对象进行比较。
当然,你可以编写一个只使用.equals
方法的正确集合,但它会很慢且不切实际。所以标准库中没有这样的实现。
此外,如果有这样的实施,它将鼓励违反Object
类合同,.hashCode
和.equals
必须相互一致。因此,它在标准库中的存在将是一个矛盾。它可能反过来伤害其他包含数据的类,这些类会担心使用不正确的.hashCode
方法处理对象,并且会觉得有必要使用.equals
- 仅将数据结构作为最小公分母,并且可能会伤到各地的表现实际上,类通常完全正确地假设其他类遵守Object
类合同,并且可以在HashSet
或HashMap
中使用它们。
对于它的价值,仅使用添加对象的Set
方法的最小的,有效的.equals
实现将是:
public class ArraySet<E> extends AbstractSet<E> {
private final ArrayList<E> list = new ArrayList<>();
@Override
public boolean add(E e) {
if (!list.contains(e)) {
list.add(e);
return true;
}
return false;
}
@Override
public Iterator<E> iterator() {
return list.iterator();
}
@Override
public int size() {
return list.size();
}
}
此套装由ArrayList
支持,并使用list.contains
在对象上调用.equals
。 AbstractSet
和AbstractCollection
的继承方法提供了Set
接口的大部分功能;例如,它的.remove
方法通过列表迭代器的.remove
方法实现。添加或删除对象或测试其在集合中的成员资格的每个操作都有O( n )复杂度,其中 n 是当前集合中的对象数,因此它不是快速或可扩展的,但它在技术上可以正常工作。
从好奇心的角度来看这很有意思,但我不建议你使用它。相反,学习如何编写哈希码方法和比较器,并享受高效集和映射的好处。
答案 1 :(得分:3)
覆盖hashCode()
时,总是覆盖equals()
。 Object
的合同明确规定两个相等的对象具有相同的哈希码,并且数量惊人的数据结构和算法依赖于此行为。添加hashCode()
并不困难,如果您现在跳过它,当您的对象开始放入基于散列的结构时,您最终会得到难以诊断的错误。
答案 2 :(得分:2)
会在数学上有意义设置除了.equals()
之外什么都不需要。
但是这样的实现会很慢(每个操作的线性时间),因此决定你总是可以给出一个提示。
无论如何,如果真的没有办法写一个hashCode()
,只要让它总是返回0,你的结构就会像你希望的那样慢!