关于Equal和HashCode的实现

时间:2021-08-05 16:02:21

 

重点提示:

Equals示例:

Public Overrides Function Equals(ByVal obj As Object) As Boolean

'Check for null and compare run-time types.

If obj Is Nothing OrElse Not [GetType]().Equals(obj.GetType()) Then

Return False

End If

Dim p As Point = CType(obj, Point)

Return x = p.x AndAlso y = p.y

End Function 'Equals


public override bool Equals(Object obj) {

//Check for null and compare run-time types.

if (obj == null || GetType() != obj.GetType()) return false;

Point p = (Point)obj;

return (x == p.x) && (y == p.y);

}

GetHashCode示例:

Public Overrides Function GetHashCode() As Integer

Return a.GetHashCode() Xor b.GetHashCode() Xor c.GetHashCode()

End Function


public override int GetHashCode () {

return a.GetHashCode() ^ b.GetHashCode() ^ c.GetHashCode();

}



首先是几篇比较有意义的内容:


equals()反映的是对象或变量具体的值。

hashCode()是对象或变量通过哈希算法计算出的哈希值。

之所以有hashCode方法,是因为在批量的对象比较中,hashCode要比equals来得快,很多集合都用到了hashCode,比如HashTable


两个obj,如果equals()相等,hashCode()一定相等。

两个obj,如果hashCode()相等,equals()不一定相等(Hash散列值有冲突的情况,虽然概率很低)。

所以:

可以考虑在集合中,判断两个对象是否相等的规则是:

第一步,如果hashCode()相等,则查看第二步,否则不相等;

第二步,查看equals()是否相等,如果相等,则两obj相等,否则还是不相等。



1、首先equals()hashcode()这两个方法都是从object类中继承过来的。

equals()是对两个对象的地址值进行的比较(即比较引用是否相同)。

hashCode()是一个本地方法,它的实现是根据本地机器相关的。


2Java语言对equals()的要求如下,这些要求是必须遵循的:

? 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”

? 反射性:x.equals(x)必须返回是“true”

? 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”

? 一致性:如果x.equals(y)返回是“true”,只要xy内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”

? 任何情况下,x.equals(null),永远返回是“false”x.equals(x不同类型的对象)永远返回是“false”


3equals()相等的两个对象,hashcode()一定相等;

反过来:hashcode()不等,一定能推出equals()也不等;

hashcode()相等,equals()可能相等,也可能不等。

引用:http://honda418.javaeye.com/blog/342731


Effective Java

讲得比较详细

equals()方法

1. 自反

2. 对称

3. 传递

4. 一致

5X非空,x.equals(null)返回false


为了实现高质量的equals()改写,effective java提醒我们要注意一下几点:

1. 使用==操作符检查是否指向同一个对象,这是一个性能优化的手段。

2. 使用instanceof来检查是否为某个类,同样也是性能优化的手段。

3. 把实参转换到正确的类型,上面一步保证了这一步不会有错误。

4. 检查类里的每一个关键域是否都相等。


hashCode()则是一种计算 对象的散列值



任何 classes 如果复写了 equals(), 便应该同时也复写 hashCode()


我的理解:

两个函数都是用来比较两个对象是否相等的。默认情况下,这两个函数通过比较两个对象的地址是否一致来判断两个对象是否相等。也就是说,两个对象只有拥有相同的引用,才可能保证equalhashcode的相同。

如果我们仅仅需要比较地址,则没必要重写这两个函数。但是现实情况是,我们有时候需要通过比较对象的值来判定两个对象是否相等。比如下面这个类:

Public Class Point

Public x as Integer

Public y as Integer

End Class

我们定义两个对象:Point1Point2对象:

Dim Point1 as Point=New Point()

Dim Point2 as Point=New Point()

Point1.x=1

Point1.y=1

Point2.x=1

Point2.y=1

很明显,Point1Point2对应相同的点(1,1),他们的值是相同的,但是如果使用equalhashcode来比较两个对象,他们却是不同的,因为他们在内存中是两个独立的对象,地址不一样。

我们需要的功能是比较值而不是地址,这时我们就需要重写Equal函数和GetHashCode函数,让他们来比较值相等。下面就是示例代码:

Equals() :

http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

Visual Basic :

Imports System


Class Point

Inherits Object

Protected x, y As Integer


Public Sub New()

Me.x = 0

Me.y = 0

End Sub 'New


Public Sub New(ByVal X As Integer, ByVal Y As Integer)

Me.x = X

Me.y = Y

End Sub 'New


Public Overrides Function Equals(ByVal obj As Object) As Boolean

'Check for null and compare run-time types.

If obj Is Nothing OrElse Not [GetType]().Equals(obj.GetType()) Then

Return False

End If

Dim p As Point = CType(obj, Point)

Return x = p.x AndAlso y = p.y

End Function 'Equals


Public Overrides Function GetHashCode() As Integer

Return x ^ y

End Function 'GetHashCode

End Class 'Point


Class Point3D

Inherits Point

Private z As Integer


Public Sub New(ByVal X As Integer, ByVal Y As Integer, ByVal Z As Integer)

Me.x = X

Me.y = Y

Me.z = Z

End Sub 'New


Public Overrides Function Equals(ByVal obj As Object) As Boolean

Return MyBase.Equals(obj) AndAlso z = CType(obj, Point3D).z

End Function 'Equals


Public Overrides Function GetHashCode() As Integer

Return MyBase.GetHashCode() ^ z

End Function 'GetHashCode

End Class 'Point3D


Class [MyClass]

Public Shared Sub Main()

Dim point2D As New Point(5, 5)

Dim point3Da As New Point3D(5, 5, 2)

Dim point3Db As New Point3D(5, 5, 2)


If Not point2D.Equals(point3Da) Then

Console.WriteLine("point2D does not equal point3Da.")

End If

If Not point3Db.Equals(point2D) Then

Console.WriteLine("Likewise, point3Db does not equal point2D.")

End If

If point3Da.Equals(point3Db) Then

Console.WriteLine("However, point3Da equals point3Db.")

End If


End Sub 'Main

End Class '[MyClass]

' ----------------------------------

' Output should be:

'

' point2D does not equal point3Da.

' Likewise, point3Db does not equal point2D.

' However, point3Da equals point3Db.



C# :

using System;


class Point: Object {

protected int x, y;


public Point() {

this.x = 0;

this.y = 0;

}


public Point(int X, int Y) {

this.x = X;

this.y = Y;

}


public override bool Equals(Object obj) {

//Check for null and compare run-time types.

if (obj == null || GetType() != obj.GetType()) return false;

Point p = (Point)obj;

return (x == p.x) && (y == p.y);

}


public override int GetHashCode() {

return x ^ y;

}

}



class Point3D: Point {

int z;


public Point3D(int X, int Y, int Z) {

this.x = X;

this.y = Y;

this.z = Z;

}


public override bool Equals(Object obj) {

return base.Equals(obj) && z == ((Point3D)obj).z;

}


public override int GetHashCode() {

return base.GetHashCode() ^ z;

}

}


class MyClass {


public static void Main() {

Point point2D = new Point(5, 5);

Point3D point3Da = new Point3D(5, 5, 2);

Point3D point3Db = new Point3D(5, 5, 2);


if (!point2D.Equals(point3Da)) {

Console.WriteLine("point2D does not equal point3Da.");

}

if (!point3Db.Equals(point2D)) {

Console.WriteLine("Likewise, point3Db does not equal point2D.");

}

if (point3Da.Equals(point3Db)) {

Console.WriteLine("However, point3Da equals point3Db.");

}


}

}

// ----------------------------------

// Output should be:

//

// point2D does not equal point3Da.

// Likewise, point3Db does not equal point2D.

// However, point3Da equals point3Db.


GetHashCode() :

http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx

Visual Basic :

Imports System


Public Class SomeType

Public Overrides Function GetHashCode() As Integer

Return 0

End Function

End Class



Public Class AnotherType

Public Overrides Function GetHashCode() As Integer

Return 1

End Function

End Class


Public Class LastType

Public Overrides Function GetHashCode() As Integer

Return 2

End Function

End Class


Public Class Demo

Private a As New SomeType()

Private b As New AnotherType()

Private c As New LastType()


Public Overrides Function GetHashCode() As Integer

Return a.GetHashCode() Xor b.GetHashCode() Xor c.GetHashCode()

End Function

End Class



C# :

using System;


public class SomeType {

public override int GetHashCode() {

return 0;

}

}


public class AnotherType {

public override int GetHashCode() {

return 1;

}

}


public class LastType {

public override int GetHashCode() {

return 2;

}

}


public class MyClass {

SomeType a = new SomeType();

AnotherType b = new AnotherType();

LastType c = new LastType();


public override int GetHashCode () {

return a.GetHashCode() ^ b.GetHashCode() ^ c.GetHashCode();

}

}


几种常见的HashCode计算方法:

http://www.javaeye.com/topic/170219

1、 把某个非零常数值,比如17,保存在一个叫result的int类型的变量中。

2、 对于对象中的关键域f(指equals方法中考虑的每一个域),完成以下步骤:

A、为该域计算int类型的散列码c:

I、如果该域是boolean类型,则计算(f ? 0 : 1)。

II、如果该域是byte、char、short或者int类型,则计算(int)f。

III、如果该域是long类型,则计算(int)(f ^ f ( f >> 32 ) )。

IV、如果该域是float类型,则计算Float.floatToInitBits( f )。

V、如果该域是double类型,则计算Double.doubleToLongBits( f )得到一个long类型的值,再执行步骤III。

VI、如果该域是一个对象引用,并且该类的equals方法通过递归调用equals的方式来比较这个域,则同样对这个递归调用hashCode。如果要求一个更为复杂的比较,则为这个域计算一个规范表示,然后针对这个范式表示调用hashCode。如果这个域为NULL,则返回0或者其他常数。

VII、如果该域是一个数组,则把每个元素当作单独的域来处理。也就是说,递归地应用上述规则,对每个重要的元素计算一个散列码,然后根据步骤B中的做法把这些散列码组合起来。

B、按照下面的公式,把上面步骤的C组合到result中:

result = 17 * result + c;

3、 返回result。

4、 写完后测试是否相等的实例具有相同的散列码。