我的对象创建会因不正确的 hashcode-equals 实现而失败吗?
Will my object creation fail with improper hashcode-equals implementation?
我想创建一个class可以通过客户编号唯一标识的客户
下面是我写的代码
public class Customer{
private Integer customerNo;
private String customerName;
public Customer(Integer customerNo, String customerName){
this.customerNo = customerNo;
this.customerName = customerName;
}
@Override
public int hashCode(){
return this.customerNo;
}
public Integer getCustomerNo(){
return this.customerNo;
}
public String getCustomerName(){
return this.customerName;
}
@Override
public boolean equals(Object o){
Customer cus = (Customer) o;
return (this.customerNo == cus.getCustomerNo() && this.customerName != null && this.customerName.equals(cus.getCustomerName()));
}
@Override
public String toString(){
StringBuffer strb = new StringBuffer();
strb.append("Customer No ")
.append(this.customerNo)
.append(", Customer Name ")
.append(this.customerName)
.append("\n");
return strb.toString();
}
public static void main(String [] args){
Set<Customer> set = null;
try{
set = new HashSet<Customer>();
set.add(new Customer(1,"Jack"));
set.add(new Customer(3,"Will"));
set.add(new Customer(1,"Tom"));
set.add(new Customer(3,"Fill"));
System.out.println("Size "+set.size());
}catch(Exception e){
e.printStackTrace();
}
}
}
从上面的代码你可以看到我返回的是我的哈希码作为客户号
而我的平等也是基于客户编号和客户名称
如果我运行上面的代码输出将是
D:\Java_Projects>java Customer
Size 4
D:\Java_Projects>
输出是为同一客户号创建的 4 个对象。
原因是即使客户没有。是一样的,只是叫法不一样
根据我上面对 'equals' 的实现,它基于 customerNo 和 customer Name。
作为 CustomerNo-CustomerName 的 4 种不同组合,因此创建了 4 个对象。
我的问题是,
我上面的哈希码实现是一种不好的做法吗?
我会遇到哪些失败?
如果我用同一个客户创建 500,000 个客户对象,会怎样?
是否会有500,000个客户对象放在同一个桶中否?
equals(...)
和 hashCode()
之间存在隐式契约:
The general contract of hashCode
is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode
method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object)
method, then calling the hashCode
method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object)
method, then calling the hashCode
method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
您的实施满足所有三个限制条件。但是,最佳做法是 equals(...)
中比较的所有属性也应该影响 hashCode()
,反之亦然。否则,使用 hashCode()
的数据结构(例如 HahsMap
和 HashSet
)可能无法以最佳方式执行。正如您提到的,一个原因是所有具有相同 hashCode
的对象都放在同一个桶中,因此访问可能没有恒定的时间复杂度。
但是,这不会导致抛出异常。
Is my above hashcode implementation a bad practise ?
假设大多数时候不同的客户有不同的 customerNo
,这是一个很好的实现。在现实世界的应用程序中,customerNo
很可能是唯一标识符,其唯一性由数据库约束保证。
What all failures I can come accross ?
你还没有处理 customerNo
是 null
的情况。这是一种方法:
public int hashCode(){
return Objects.hash(customerNo);
}
当 customerNo
为 null
时,这将 return 0。
您在 equals
方法中还有另一个错误:Integer
对象不应与 ==
进行比较,这会给您带来意想不到的结果。此外,customerName
设置为 null
的两个客户永远不会相等。 Objects.equals
方法解决了这些问题。
return Objects.equals(this.customerNo, cus.customerNo)
&& Objects.equals(this.customerName, cus.customerName);
What if I create 500,000 Customer objects with same customer No, what will happen ?
Whether there will be 500,000 customer objects placed in a same bucket No ?
在这种情况下,所有对象确实会放在同一个桶中。您的 HashSet
被缩减为一个链表数据结构,它的性能会很差:要定位客户对象,数据结构必须在最坏的情况下将给定对象与每个对象进行比较。
如果Customer
实施Comparable
,散列table桶可以使用二叉搜索树而不是链表,并且性能不会受到同样严重的影响。
我想创建一个class可以通过客户编号唯一标识的客户
下面是我写的代码
public class Customer{
private Integer customerNo;
private String customerName;
public Customer(Integer customerNo, String customerName){
this.customerNo = customerNo;
this.customerName = customerName;
}
@Override
public int hashCode(){
return this.customerNo;
}
public Integer getCustomerNo(){
return this.customerNo;
}
public String getCustomerName(){
return this.customerName;
}
@Override
public boolean equals(Object o){
Customer cus = (Customer) o;
return (this.customerNo == cus.getCustomerNo() && this.customerName != null && this.customerName.equals(cus.getCustomerName()));
}
@Override
public String toString(){
StringBuffer strb = new StringBuffer();
strb.append("Customer No ")
.append(this.customerNo)
.append(", Customer Name ")
.append(this.customerName)
.append("\n");
return strb.toString();
}
public static void main(String [] args){
Set<Customer> set = null;
try{
set = new HashSet<Customer>();
set.add(new Customer(1,"Jack"));
set.add(new Customer(3,"Will"));
set.add(new Customer(1,"Tom"));
set.add(new Customer(3,"Fill"));
System.out.println("Size "+set.size());
}catch(Exception e){
e.printStackTrace();
}
}
}
从上面的代码你可以看到我返回的是我的哈希码作为客户号
而我的平等也是基于客户编号和客户名称
如果我运行上面的代码输出将是
D:\Java_Projects>java Customer
Size 4
D:\Java_Projects>
输出是为同一客户号创建的 4 个对象。 原因是即使客户没有。是一样的,只是叫法不一样 根据我上面对 'equals' 的实现,它基于 customerNo 和 customer Name。 作为 CustomerNo-CustomerName 的 4 种不同组合,因此创建了 4 个对象。
我的问题是,
我上面的哈希码实现是一种不好的做法吗?
我会遇到哪些失败?
如果我用同一个客户创建 500,000 个客户对象,会怎样?
是否会有500,000个客户对象放在同一个桶中否?
equals(...)
和 hashCode()
之间存在隐式契约:
The general contract of
hashCode
is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the
hashCode
method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.If two objects are equal according to the
equals(Object)
method, then calling thehashCode
method on each of the two objects must produce the same integer result.It is not required that if two objects are unequal according to the
equals(java.lang.Object)
method, then calling thehashCode
method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
您的实施满足所有三个限制条件。但是,最佳做法是 equals(...)
中比较的所有属性也应该影响 hashCode()
,反之亦然。否则,使用 hashCode()
的数据结构(例如 HahsMap
和 HashSet
)可能无法以最佳方式执行。正如您提到的,一个原因是所有具有相同 hashCode
的对象都放在同一个桶中,因此访问可能没有恒定的时间复杂度。
但是,这不会导致抛出异常。
Is my above hashcode implementation a bad practise ?
假设大多数时候不同的客户有不同的 customerNo
,这是一个很好的实现。在现实世界的应用程序中,customerNo
很可能是唯一标识符,其唯一性由数据库约束保证。
What all failures I can come accross ?
你还没有处理 customerNo
是 null
的情况。这是一种方法:
public int hashCode(){
return Objects.hash(customerNo);
}
当 customerNo
为 null
时,这将 return 0。
您在 equals
方法中还有另一个错误:Integer
对象不应与 ==
进行比较,这会给您带来意想不到的结果。此外,customerName
设置为 null
的两个客户永远不会相等。 Objects.equals
方法解决了这些问题。
return Objects.equals(this.customerNo, cus.customerNo)
&& Objects.equals(this.customerName, cus.customerName);
What if I create 500,000 Customer objects with same customer No, what will happen ? Whether there will be 500,000 customer objects placed in a same bucket No ?
在这种情况下,所有对象确实会放在同一个桶中。您的 HashSet
被缩减为一个链表数据结构,它的性能会很差:要定位客户对象,数据结构必须在最坏的情况下将给定对象与每个对象进行比较。
如果Customer
实施Comparable
,散列table桶可以使用二叉搜索树而不是链表,并且性能不会受到同样严重的影响。