比较对象
导航 聚合 主题: ) |
在 Java 中,我们可以区分两种类型的等式。
- 对象引用等式:当两个对象引用指向同一个对象时。
- 对象值等式:当两个独立的对象恰好具有相同的数值/状态时。
如果两个对象在引用方面相等,它们在值方面也相等。
==
运算符可用于检查两个对象引用是否指向同一个对象。
代码部分 5.19:引用等式。
if (objRef1 == objRef2) {
// The two object references point to the same object
}
|
为了能够比较同一个类的两个 Java 对象,
方法必须被该类覆盖并实现。boolean
equals(Object
obj)
实现者决定哪些值必须相等才能认为两个对象相等。例如,在下面的类中,name
和 address
必须相等,但 description
不必。
代码清单 5.5:Customer.java
public class Customer {
private String name;
private String address;
private String description;
// ...
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (obj instanceof Customer cust) {
if ( ((cust.getName() == null && name == null) || cust.getName().equals(name))
&& ((cust.getAddress() == null && address == null) || cust.getAddress().equals(address))
) {
return true;
}
}
return false;
}
}
|
在 equals()
方法被覆盖后,来自同一个类的两个对象可以这样比较
代码部分 5.20:方法用法。
Customer cust1 = new Customer();
Customer cust2 = new Customer();
//...
if (cust1.equals(cust2)) {
// Two Customers are equal, by name and address
}
|
注意,相等的对象必须具有相等的哈希码。因此,当覆盖 equals
方法时,您也必须覆盖 hashCode
方法。未能做到这一点将违反 hashCode
方法的一般约定,并且任何使用哈希码的类(例如 HashMap
)将无法正常运行。
在 Java 中,有几种现有的方法已经对来自任何类的对象进行了排序,例如 Collections.sort(List<T> list)
。但是,Java 需要知道两个对象之间的比较规则。因此,当您定义一个新类并希望您的类的对象可排序时,您必须实现 Comparable
并重新定义 compareTo(Object obj)
方法。
int
compareTo(T o)- 比较两个对象并返回一个整数
- 负整数表示当前对象在自然排序中位于参数对象之前。
- 零表示当前对象和参数对象相等。
- 正整数表示当前对象在自然排序中位于参数对象之后。
假设名称比地址更重要,而描述被忽略。
代码清单 5.6:SortableCustomer.java
public class SortableCustomer implements Comparable<SortableCustomer> {
private String name;
private String address;
private String description;
// ...
public int compareTo(SortableCustomer anotherCustomer) {
if (name.compareTo(anotherCustomer.getName()) == 0) {
return address.compareTo(anotherCustomer.getAddress());
} else {
return name.compareTo(anotherCustomer.getName());
}
}
}
|
实现此接口的对象可以用作排序地图中的键或排序集中的元素,而无需指定比较器。
如果且仅当对于类 C 的每个 e1 和 e2,e1.compareTo((Object) e2) == 0
与 e1.equals((Object) e2)
具有相同的布尔值,则类 C 的自然排序被认为与 equals 一致。注意,null 不是任何类的实例,并且 e.compareTo(null)
应该抛出一个 NullPointerException,即使 e.equals(null)
返回 false。
强烈建议(但不是必需)自然排序与 equals 一致。这是因为没有显式比较器的排序集(和排序地图)在与自然排序与 equals 不一致的元素(或键)一起使用时,行为“奇怪”。特别是,这样的排序集(或排序地图)违反了集合(或地图)的一般约定,集合(或地图)的一般约定是在 equals 方法的定义中定义的。
有时我们可能希望更改来自同一个类的对象集合的排序方式。我们可能希望按降序或升序排序。我们可能希望按name
或 address
排序。
我们需要为每种排序方式创建一个类。它必须实现 Comparator
接口。
从 Java 5.0 开始,Comparator
接口是泛型的;这意味着当您实现它时,您可以指定比较器可以比较的对象类型。
代码清单 5.7:CustomerComparator.java
public class CustomerComparator implements Comparator<Customer> {
public int compare(Customer cust1, Customer cust2) {
return cust1.getName().compareTo(cust2.getName());
}
}
|
上面的类然后可以与 SortedSet 或其他支持排序的集合关联。
代码部分 5.21:比较器用法。
Collection<Customer> orderedCustomers = new TreeSet<Customer>(new CustomerComparator());
|
使用迭代器,可以按name
排序的顺序遍历 orderedCustomers
集合。
列表可以通过 Collections
的 sort
方法进行排序。
代码部分 5.22:自定义比较。
java.util.Collections.sort(custList, new CustomerComparator());
|
根据指定比较器引起的顺序,对指定列表进行排序。列表中的所有元素都必须使用指定的比较器相互比较。
对象数组也可以在 Comparator
的帮助下进行排序。
代码部分 5.23:数组排序。
SortableCustomer[] customerArray;
//...
java.util.Arrays.sort(customerArray, new CustomerComparator());
|
根据指定比较器引起的顺序,对指定的 Customer
对象数组(customerArray)进行排序。数组中的所有元素都必须使用指定的比较器相互比较。