hashCode 和 equals 列表 属性
hashCode and equals with a list property
我有一个 class,在 equals/hashCode:
中有一个 for 循环
class User {
private List<Task> tasks;
private ZonedDateTime date;
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + (date != null ? date() : 0);
for (var task : tasks) {
hash = 31 * hash + task.hashCode();
}
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
final User other = (User) obj;
if (tasks.size() != other.tasks.size()) return false;
// needed?
for (int i = 0; i < tasks.size(); i++) {
if (!tasks.get(i).equals(other.tasks.get(i))) {
return false;
}
}
return Objects.equals(timeStamp, other.timeStamp) && Objects.equals(tasks, other. tasks);
}
}
我习惯有equals/hashCode的这个版本(版本2),更短更快:
@Override
public int hashCode() {
return Objects.hash(date, tasks);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
final User other = (User) obj;
return Objects.equals(timeStamp, other.timeStamp) && Objects.equals(tasks, other. tasks);
}
我可以用版本 2 替换以前的 equals/hashCode 而不必担心正确性吗?
两个版本return结果相同吗?
总结一下:
对于典型的 List 实现,我们可以使用版本 2 而不是版本 1。
一个与此相关的附加问题:
如果 属性 task
不是 List
而是 Stream
, 版本 2 是否也有效? (Stream<Task> tasks
).
版本 2 可以正常工作,尽管它 return 哈希码略有不同。
这取决于具体的List
实现。
让我们看看 Object.equals
做了什么:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
所以它会检查一些简单的情况,然后调用 a.equals(b)
,这意味着它将调用列表的 equals
方法。但是,如果您使用的是一些自定义 List
或只是一些不对元素进行一一比较的列表,那么这两个实现将不同。
对于任何合理的实现,equals
应该遍历元素并使用 equals
比较每个元素。这就是 AbstractList
所做的。
另请注意,您的哈希码可能会在不同实现之间发生变化。
我有一个 class,在 equals/hashCode:
中有一个 for 循环class User {
private List<Task> tasks;
private ZonedDateTime date;
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + (date != null ? date() : 0);
for (var task : tasks) {
hash = 31 * hash + task.hashCode();
}
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
final User other = (User) obj;
if (tasks.size() != other.tasks.size()) return false;
// needed?
for (int i = 0; i < tasks.size(); i++) {
if (!tasks.get(i).equals(other.tasks.get(i))) {
return false;
}
}
return Objects.equals(timeStamp, other.timeStamp) && Objects.equals(tasks, other. tasks);
}
}
我习惯有equals/hashCode的这个版本(版本2),更短更快:
@Override
public int hashCode() {
return Objects.hash(date, tasks);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
final User other = (User) obj;
return Objects.equals(timeStamp, other.timeStamp) && Objects.equals(tasks, other. tasks);
}
我可以用版本 2 替换以前的 equals/hashCode 而不必担心正确性吗?
两个版本return结果相同吗?
总结一下:
对于典型的 List 实现,我们可以使用版本 2 而不是版本 1。
一个与此相关的附加问题:
如果 属性 task
不是 List
而是 Stream
, 版本 2 是否也有效? (Stream<Task> tasks
).
版本 2 可以正常工作,尽管它 return 哈希码略有不同。
这取决于具体的List
实现。
让我们看看 Object.equals
做了什么:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
所以它会检查一些简单的情况,然后调用 a.equals(b)
,这意味着它将调用列表的 equals
方法。但是,如果您使用的是一些自定义 List
或只是一些不对元素进行一一比较的列表,那么这两个实现将不同。
对于任何合理的实现,equals
应该遍历元素并使用 equals
比较每个元素。这就是 AbstractList
所做的。
另请注意,您的哈希码可能会在不同实现之间发生变化。