Comparator的JavaDoc声明
强烈建议(尽管不要求)自然排序与等于一致。
他们还举了一个例子,一个"奇怪的" (a.equals(b) && c.compare(a,b) != 0)
时的行为。
现在,有人能举例说明一个"奇怪的"案件(!a.equals(b) && c.compare(a,b) == 0)
的行为?第二种情况应该比第一种情况更频繁发生,因为在实施equals
时很容易忘记为比较类型实施Comparator
。在不知道例如TreeSet
的实现的情况下,很难想出一个例子。
(这个问题与我有关,这是一个较长的故事。而且这不是作业)
答案 0 :(得分:1)
这是一个简单的演示。我们有一个名为Strange
的类,它使用不区分大小写的字符串比较来实现equals
和hashCode
,但实现compareTo
区分大小写。
class Strange implements Comparable<Strange> {
final String s;
public Strange(String s) {
this.s = s;
}
@Override
public boolean equals(Object o) {
// Kind of equals - case insensitive.
return (o instanceof Strange) && ((Strange) o).s.equalsIgnoreCase(s);
}
@Override
public int hashCode() {
// Consistent with equals.
return s.toUpperCase().hashCode();
}
@Override
public int compareTo(Strange o) {
// Exact ordering including case - inconsistent with equals.
return s.compareTo(o.s);
}
@Override
public String toString() {
return s;
}
}
public void test() {
Set<Strange> set1 = new HashSet<>();
Set<Strange> set2 = new TreeSet<>();
for (String s : new String[]{"Hello", "hello", "Everyone", "everyone"}) {
Strange strange = new Strange(s);
set1.add(strange);
set2.add(strange);
}
System.out.println("Set1: " + set1);
System.out.println("Set2: " + set2);
}
我们得到 - 正如您所料:
Set1: [Hello, Everyone]
Set2: [Everyone, Hello, everyone, hello]
了解如何将字符串放入TreeSet
更改结果?这是因为TreeSet
使用compareTo
而HashSet
使用equals
和hashCode
。这可能会破坏许多不同(以及最重要的意外)方式,因为您不必知道幕后使用的是哪种Set
。
这表明(a.equals(b) && a.compareTo(b) != 0)
给出了奇怪的结果。很容易证明相反的问题(!a.equals(b) && a.compareTo(b) == 0)
也表现出奇怪的结果。
答案 1 :(得分:0)
Collections的JDK实现依赖于这样的行为关系。另一个很好的例子是HashSet,它依赖于equals()
和hashCode()
同意。
奇怪&#34;他们的意思是&#34; undefined&#34; - 如果这些类与不遵守规则的类一起工作,则没有定义这些类的行为方式。它们可能完美地工作,但它们可能不会。但是如果您的元素类不遵守其javadoc中描述的行为,则不能依赖它们正常工作。
答案 2 :(得分:0)
假设以下API:
final class Foo {
int bar;
Foo(int bar) { this.bar = bar; }
public int hashCode() {
return bar;
}
public boolean equals(Object o) {
return o instanceof Foo && ((Foo)o).bar == bar;
}
}
static Set<Foo> getSetOpaquely() {???}
我不知道Set的来源,只是我需要使用它。
假设我假设Set与HashSet类似,并以equals
。
他们还举了一个例子,一个&#34;奇怪的&#34;
时的行为(a.equals(b) && c.compare(a,b) != 0)
假设我
Set<Foo> mySet = getSetOpaquely();
mySet.add(new Foo(1));
System.out.println(mySet.add(new Foo(1));
假设我打印true
时会感到惊讶,因为它是带有比较器的TreeSet
(lhs, rhs) -> lhs == rhs ? 0 : 1
现在,有人能举例说明一个&#34;奇怪的&#34;行为
(!a.equals(b) && c.compare(a,b) == 0)
?
假设我
Set<Foo> mySet = getSetOpaquely();
mySet.add(new Foo(102));
System.out.println(mySet.add(new Foo(12));
假设我打印false
时会感到惊讶,因为它是带有比较器的TreeSet
(lhs, rhs) -> Integer.compare(lhs.bar % 10, rhs.bar % 10)
现在,定义与equals
不一致的排序不存在固有问题。关键是 TreeSet的行为可能与Set 的文档中指定的行为不同。
[...]
Set
接口是根据等于操作定义的,但TreeSet
实例使用其compareTo
(或compare
执行所有元素比较)方法,因此从该方法的观点来看,通过该方法被认为相等的两个元素是相等的。 集合的行为定义良好,即使其排序与equals不一致;它只是不遵守Set
界面的一般合同。
只要比较者不是hacky并且您知道它是具有特定排序的TreeSet,您就不会感到惊讶。 (如果它像(lhs, rhs) -> 1
一样hacky,你可能会感到惊讶。)