为 BST 实现 equals 和 hashcode
Implementing equals and hashcode for a BST
这个问题是 的后续问题。我的问题没有经过深思熟虑,所以我得到了一个我不确定如何使用的答案。
我需要为一个BST实现equals
:使得iff两个BST在结构和内容上都相等,那么equals
returns 真的。因此,我想我还需要实现 hashCode
函数。我得到了 hashCode 函数的答案,使得树的结构和内容相同。
@Override
puclic int hashCode(){
int h = Objects.hashCode(data);//data is int
int child=0;
if(null != left)
child =left.hashCode();
if(null != right)
child+= right.hashCode();
if(0<child) h= h*31+child;
return h;
}
但是我该如何实现equals函数呢?下面的工作是否 iff 树在结构和内容上都相同?
@Override
public boolean equals(Node otherRoot){
return root.hashCode() == otherRoot.hashCode();
}
在某些情况下我可能会误报吗?
或者我的 hashCode 应该是
@Override
public int hashCode(){
int h = contents.hashCode();
h = h * 31 + Objects.hashCode(leftChild);
h = h * 31 + Objects.hashCode(rightChild);
return h;
}
在后一种情况下,我的 equals
会避免误报吗?
不确定对象是什么,但你最后一个 hashCode() 示例需要处理 null,我想是这样的:
@Override
public int hashCode() {
int h = contents.hashCode();
if (leftChild != null) h = h* 31 + leftChild.hashCode();
if (rightChild != null) h = h * 31 + rightChild.hashCode();
return h;
}
如果树足够深,我可以看到溢出的 h,所有的 h * 31。
hashCode 的 contract 不保证相等,因此您可能需要一直沿树向下调用 equals 以确保一切平衡。
Will the following work iff the trees are equal in both structure and content? root.hashCode() == otherRoot.hashCode()
不,这是行不通的,因为哈希码相等是一条单行道:当对象相等时,哈希码必须相等。但是,当对象不相等时,哈希码可能相等也可能不相等。一旦您应用 pigeonhole principle,这就有意义了:可能的哈希码数量约为 4B,而可能的 BST 数量几乎是无限的。
您可以采用与构建哈希码相同的方式构建比较 - 即递归:
- 检查正在比较的节点的值是否彼此相等。如果值不同,return
false
- 检查两个节点是否都有左子树。如果其中一个有左子树,另一个没有,return
false
- 检查两个节点是否都有右子树。如果其中一个有右子树,另一个没有,return
false
- 递归地对左子树应用
equals
。如果结果是 false
, return false
- 递归地对右子树应用
equals
。如果结果是 false
, return false
- Return
true
我还没有完全测试过这个,但这里是开始的地方
public boolean equals(Object o) {
// exact same object
if(this === o) {
return true;
}
if(!o instanceof Node) {
return false
}
Node otherTree = (Node) o;
boolean selfHasLeft = this.left == null,
selfHasRight = this.right == null,
otherHasLeft = otherTree.left == null,
otherHasRight = otherTree.right == null;
// this tree must have the same children as the other tree
if(selfHasLeft != otherHasLeft || selfHasRight != otherHasRight) {
return false;
}
// must have same value
if(this.value != other.value) {
return false;
}
// if they have no children then now they should be the same tree
// otherwise, check that their children are the same
if(!selfHasLeft && !selfHasRight) {
return true;
} else if(selfHasLeft && !selfHasRight) {
return this.left.equals(otherTree.left);
} else if(selfHasRight && !selfHasLeft) {
return this.right.equals(otherTree.right);
} else {
return this.left.equals(otherTree.left) && this.right.equals(otherTree.right);
}
}
你的第二个 hashCode
实现对我来说看起来不错,但是当可能对象的数量大于 int
的范围时你永远无法避免哈希码冲突 - 这里就是这种情况所以你不应该在 equals
.
中使用哈希码
你应该做的是这样的(假设class名字是BST
):
public boolean equals(Object other) {
if(this == other) {
return true;
}
if(!(other instanceof BST)) {
// If other is null we will end up here
return false;
}
BST bst = (BST) other;
// Check equality of the left child
if(left != null) {
if(!left.equals(other.left)) {
// Left childs aren't equal
return false;
}
} else if (other.left != null) {
// this.left is null but other.left isn't
return false;
}
// Check equality of the right child
if(right != null) {
if(!right.equals(other.right)) {
// Right childs aren't equal
return false;
}
} else if (other.right != null) {
// this.right is null but other.right isn't
return false;
}
// Both left and right childs are equal
return true;
}
这个问题是
我需要为一个BST实现equals
:使得iff两个BST在结构和内容上都相等,那么equals
returns 真的。因此,我想我还需要实现 hashCode
函数。我得到了 hashCode 函数的答案,使得树的结构和内容相同。
@Override
puclic int hashCode(){
int h = Objects.hashCode(data);//data is int
int child=0;
if(null != left)
child =left.hashCode();
if(null != right)
child+= right.hashCode();
if(0<child) h= h*31+child;
return h;
}
但是我该如何实现equals函数呢?下面的工作是否 iff 树在结构和内容上都相同?
@Override
public boolean equals(Node otherRoot){
return root.hashCode() == otherRoot.hashCode();
}
在某些情况下我可能会误报吗?
或者我的 hashCode 应该是
@Override
public int hashCode(){
int h = contents.hashCode();
h = h * 31 + Objects.hashCode(leftChild);
h = h * 31 + Objects.hashCode(rightChild);
return h;
}
在后一种情况下,我的 equals
会避免误报吗?
不确定对象是什么,但你最后一个 hashCode() 示例需要处理 null,我想是这样的:
@Override
public int hashCode() {
int h = contents.hashCode();
if (leftChild != null) h = h* 31 + leftChild.hashCode();
if (rightChild != null) h = h * 31 + rightChild.hashCode();
return h;
}
如果树足够深,我可以看到溢出的 h,所有的 h * 31。
hashCode 的 contract 不保证相等,因此您可能需要一直沿树向下调用 equals 以确保一切平衡。
Will the following work iff the trees are equal in both structure and content?
root.hashCode() == otherRoot.hashCode()
不,这是行不通的,因为哈希码相等是一条单行道:当对象相等时,哈希码必须相等。但是,当对象不相等时,哈希码可能相等也可能不相等。一旦您应用 pigeonhole principle,这就有意义了:可能的哈希码数量约为 4B,而可能的 BST 数量几乎是无限的。
您可以采用与构建哈希码相同的方式构建比较 - 即递归:
- 检查正在比较的节点的值是否彼此相等。如果值不同,return
false
- 检查两个节点是否都有左子树。如果其中一个有左子树,另一个没有,return
false
- 检查两个节点是否都有右子树。如果其中一个有右子树,另一个没有,return
false
- 递归地对左子树应用
equals
。如果结果是false
, returnfalse
- 递归地对右子树应用
equals
。如果结果是false
, returnfalse
- Return
true
我还没有完全测试过这个,但这里是开始的地方
public boolean equals(Object o) {
// exact same object
if(this === o) {
return true;
}
if(!o instanceof Node) {
return false
}
Node otherTree = (Node) o;
boolean selfHasLeft = this.left == null,
selfHasRight = this.right == null,
otherHasLeft = otherTree.left == null,
otherHasRight = otherTree.right == null;
// this tree must have the same children as the other tree
if(selfHasLeft != otherHasLeft || selfHasRight != otherHasRight) {
return false;
}
// must have same value
if(this.value != other.value) {
return false;
}
// if they have no children then now they should be the same tree
// otherwise, check that their children are the same
if(!selfHasLeft && !selfHasRight) {
return true;
} else if(selfHasLeft && !selfHasRight) {
return this.left.equals(otherTree.left);
} else if(selfHasRight && !selfHasLeft) {
return this.right.equals(otherTree.right);
} else {
return this.left.equals(otherTree.left) && this.right.equals(otherTree.right);
}
}
你的第二个 hashCode
实现对我来说看起来不错,但是当可能对象的数量大于 int
的范围时你永远无法避免哈希码冲突 - 这里就是这种情况所以你不应该在 equals
.
你应该做的是这样的(假设class名字是BST
):
public boolean equals(Object other) {
if(this == other) {
return true;
}
if(!(other instanceof BST)) {
// If other is null we will end up here
return false;
}
BST bst = (BST) other;
// Check equality of the left child
if(left != null) {
if(!left.equals(other.left)) {
// Left childs aren't equal
return false;
}
} else if (other.left != null) {
// this.left is null but other.left isn't
return false;
}
// Check equality of the right child
if(right != null) {
if(!right.equals(other.right)) {
// Right childs aren't equal
return false;
}
} else if (other.right != null) {
// this.right is null but other.right isn't
return false;
}
// Both left and right childs are equal
return true;
}