Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/366.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 比较与相等一致意味着什么?如果我的班级没有';你不遵循这个原则吗?_Java_Comparison_Equals_Comparator_Comparable - Fatal编程技术网

Java 比较与相等一致意味着什么?如果我的班级没有';你不遵循这个原则吗?

Java 比较与相等一致意味着什么?如果我的班级没有';你不遵循这个原则吗?,java,comparison,equals,comparator,comparable,Java,Comparison,Equals,Comparator,Comparable,从TreeMap的JavaDoc: 请注意,排序由排序映射维护(无论是否为 如果提供了显式比较器,则必须与equals一致 此排序映射用于正确实现映射接口。(见 可比比较器,用于精确定义符合 这是因为映射接口是根据 equals操作,但映射使用其 compareTo(或compare)方法,因此 从排序图的角度来看,此方法是相等的。这个 已排序映射的行为是定义良好的,即使其顺序是 与平等不一致;它就是不遵守总合同 地图界面的 有人能举一个具体的例子来说明如果排序与equals不一致可能出现的问题

从TreeMap的JavaDoc:

请注意,排序由排序映射维护(无论是否为 如果提供了显式比较器,则必须与equals一致 此排序映射用于正确实现映射接口。(见 可比比较器,用于精确定义符合 这是因为映射接口是根据 equals操作,但映射使用其 compareTo(或compare)方法,因此 从排序图的角度来看,此方法是相等的。这个 已排序映射的行为是定义良好的,即使其顺序是 与平等不一致;它就是不遵守总合同 地图界面的

有人能举一个具体的例子来说明如果排序与equals不一致可能出现的问题吗?以具有自然顺序的用户定义类为例,即它实现了Comparable。JDK中的所有内部类也都保持这个不变吗

允许不一致的行为:

强烈建议(尽管不是必需的)自然顺序与equals一致

因此在理论上,JDK中的一个类可能有一个
compareTo
equals
不一致。一个很好的例子是

下面是一个与equals不一致的比较器的人为示例(它基本上说所有字符串都是相等的)

输出:

                  Random Order: [{5, 500}, {4, 100}, {3, 200}, {2, 100}, {1, 100}]
  Non Consistent and Non Total: [{5, 500}, {3, 200}, {4, 100}]
      Consistent but Not Total: [{5, 500}, {3, 200}, {4, 100}]
          Consistent and Total: [{5, 500}, {3, 200}, {1, 100}, {2, 100}, {4, 100}]
尺寸:1
内容:{a=b}

publicstaticvoidmain(字符串[]args){
Map brokenMap=newtreemap(newcomparator(){
@凌驾
公共整数比较(字符串o1、字符串o2){
返回0;
}
});
brokenMap.put(“a”、“a”);
brokenMap.put(“b”、“b”);
System.out.println(“大小:+brokenMap.size());
System.out.println(“内容:+brokenMap”);
}

假设我们有一个简单的
学生
类实现了
可比
,但没有覆盖
equals()
/
hashCode()
。当然,
equals()
compareTo()
不一致-两个年龄相同的不同学生不相等:

class Student implements Comparable<Student> {

    private final int age;

    Student(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }

    @Override
    public String toString() {
        return "Student(" + age + ")";
    }
}
显然,由于散列,项的枚举返回“随机”顺序-这很好,它不违反任何
Map
契约。但最后一句话完全被打破了。因为
HashMap
使用
equals()
/
hashCode()
来比较实例,所以通过
newstudent(22)
键获取值失败并返回
null

这就是JavaDoc试图解释的:此类类将与
TreeMap
一起工作,但可能无法与其他
Map
实现一起工作。请注意,
Map
操作是根据
equals()
/
hashCode()
记录和定义的,例如:

[…]当且仅当此映射包含键k的映射,使得
(key==null?k==null:key.equals(k))


因此,我不相信有任何标准的JDK类实现了
compariable
,但未能实现
equals()
/
hashCode()
对。

这里是另一个例子,说明了equals总顺序的一致性对于实现非常重要

假设我们有一个对象
MyObject
,它有两个字段:
id
quantity
。 顾名思义,
id
是对象的自然键,
quantity
只是一个属性

public class MyObject {
  int id;
  int quantity;
  ...
}
让我们设想一下,我们希望使用一组按数量降序排序的
MyObject
。 我们可以编写的第一个比较器是:

Comparator<MyObject> naiveComp = new Comparator<MyObject>() {
  @Override
  public int compare(MyObject o1, MyObject o2) {
    return o2.quantity - o1.quantity;
  }
};
然而,这再次无法适应TreeSet/TreeMap!(参见下面的完整代码) 这是因为排序关系不是总计,即任何两个对象都不能严格地放入排序关系中。在这个比较器中,当
数量
字段相等时,结果的顺序是不确定的

更好的比较标准是:

Comparator<MyObject> betterComp = new Comparator<MyObject>() {
  @Override
  public int compare(MyObject o1, MyObject o2) {
    if (o1.equals(o2)) {
      return 0;
    }
    if (o1.quantity == o2.quantity) {
      return o1.id - o2.id; // never 0
    }
    return o2.quantity - o1.quantity; // never 0
  }
};
输出:

                  Random Order: [{5, 500}, {4, 100}, {3, 200}, {2, 100}, {1, 100}]
  Non Consistent and Non Total: [{5, 500}, {3, 200}, {4, 100}]
      Consistent but Not Total: [{5, 500}, {3, 200}, {4, 100}]
          Consistent and Total: [{5, 500}, {3, 200}, {1, 100}, {2, 100}, {4, 100}]
结论:

虽然我认为从概念上把身份和顺序分开是非常合理的。例如,在关系数据库术语中:

select * from MyObjects order by quantity
工作完美。在这里,我们不关心对象标识,也不需要总排序

但是,由于基于树的集合实现中的限制,必须确保它们编写的任何比较器:

  • 一致性就是平等
  • 提供所有可能对象的总排序

下面是一个简单但现实的例子,说明如果比较方法与equals不一致,会发生什么。在JDK中,
BigDecimal
实现了
Comparable
,但其比较方法与equals不一致。例如:

> BigDecimal z = new BigDecimal("0.0")
> BigDecimal zz = new BigDecimal("0.00")
> z.compareTo(zz)
0
> z.equals(zz)
false
这是因为
BigDecimal
的比较方法只考虑数值,而
equals
也考虑精度。由于
0.0
0.00
具有不同的精度,因此即使它们具有相同的数值,它们也是不相等的

下面是一个例子,说明
树集
违反
的总合同意味着什么。(与
TreeMap
Map
的情况相同,但使用集合更容易演示。)让我们将
contains
的结果与从集合中取出元素并调用
equals
的结果进行比较:

> TreeSet<BigDecimal> ts = new TreeSet<>()
> ts.add(z)
> ts.contains(z)
true
> z.equals(ts.iterator().next())
true
> ts.contains(zz)
true
> zz.equals(ts.iterator().next())
false
这很奇怪。我们有两个相等的集合,但一个集合表示它包含一个对象,而另一个集合表示它不包含相同的对象。同样,这反映了
TreeSet
使用比较方法,而
HashSet
使用
equals
的事实

                  Random Order: [{5, 500}, {4, 100}, {3, 200}, {2, 100}, {1, 100}]
  Non Consistent and Non Total: [{5, 500}, {3, 200}, {4, 100}]
      Consistent but Not Total: [{5, 500}, {3, 200}, {4, 100}]
          Consistent and Total: [{5, 500}, {3, 200}, {1, 100}, {2, 100}, {4, 100}]
select * from MyObjects order by quantity
> BigDecimal z = new BigDecimal("0.0")
> BigDecimal zz = new BigDecimal("0.00")
> z.compareTo(zz)
0
> z.equals(zz)
false
> TreeSet<BigDecimal> ts = new TreeSet<>()
> ts.add(z)
> ts.contains(z)
true
> z.equals(ts.iterator().next())
true
> ts.contains(zz)
true
> zz.equals(ts.iterator().next())
false
> HashSet<BigDecimal> hs = new HashSet<>(ts)
> hs.equals(ts)
true
> ts.contains(zz)
true
> hs.contains(zz)
false
> HashSet<BigDecimal> hs2 = new HashSet<>()
> hs2.add(zz)
> ts.equals(hs2)
true
> hs2.equals(ts)
false
set1.equals(set2)
ts.equals(hs2)
hs2.equals(ts)