检测Class是否相等
在C#的容器中,常用的三个容器数组,ArrayList,Hashtable..数组比较简单,实现某种单一数据的存储,但是并不能自由插入,移除和容纳不同的对象..,所以ArrayList是数组的替代品,并且由于ArrayList可以自由的添加,删除,插入,读取,给我们提供了足够大的自由性,颇得我的青睐..不过使用中,难免有些缺点,感觉最麻烦的就是检测某对象是否在Items中..因为每一个new出来的Class在内存中的表现不相同,即便是同一个类,你new出来两个,然后再判断,也是会一样的!!所以每次使用ArrayList.Contains()检测对象的时候,难免都得不到自己想要的结果..因为每个Class都是继承自Object类..而ArrayList.Contains()的实现是IList.Contains,而此方法是调用Class中的Equals方法判断是否相等,这个时候,可以在自己的对象中覆写Object.Equals方法,以达到自己的目的..注意,如果你覆写了Equals方法,则也要覆写GetHashCode(),因为Equals是用获取Object.GetHashCode()来做判断的.看看下面的代码就明白:
1
using System;
2
3
namespace HashCode_Test
4
{
5
/// <summary>
6
/// 给你的类加入Equals,测试类
7
/// </summary>
8
public class Class2
9
{
10
int myHashCode = 0;
11
public Class2( int id ) //传递进一个int,作为GetHashCode的值
12
{
13
myHashCode = id;
14
}
15
16
//覆写GetHashCode,关键的一步
17
public override int GetHashCode()
18
{
19
return myHashCode;
20
}
21
22
//这步,可有可无,主要是做测试结果用的
23
public override string ToString()
24
{
25
return DateTime.Now.ToString();
26
}
27
28
/// <summary>
29
/// 重载了Equals方法,这步和GetHashCode配合起来才会有效果
30
/// </summary>
31
/// <param name="o">要检测的对象</param>
32
/// <returns>返回是否相同</returns>
33
public override bool Equals( object o )
34
{
35
return o.GetHashCode() == myHashCode;
36
}
37
38
//在这里使用运算符重载,主要是为了进一步演示Equals
39
public static bool operator ==(object c1, Class2 c2)
40
{
41
return c1.GetHashCode().Equals( c2.GetHashCode() );
42
}
43
44
//当你重载了==运算符后,必须要重载!=运算符
45
public static bool operator !=(object c1, Class2 c2)
46
{
47
return c1.GetHashCode().Equals( c2.GetHashCode() );
48
}
49
50
}
51
}
using System;2

3
namespace HashCode_Test4
{5
/// <summary>6
/// 给你的类加入Equals,测试类7
/// </summary>8
public class Class29
{10
int myHashCode = 0;11
public Class2( int id ) //传递进一个int,作为GetHashCode的值12
{13
myHashCode = id;14
}15
16
//覆写GetHashCode,关键的一步17
public override int GetHashCode() 18
{19
return myHashCode;20
}21
22
//这步,可有可无,主要是做测试结果用的23
public override string ToString() 24
{25
return DateTime.Now.ToString();26
}27
28
/// <summary>29
/// 重载了Equals方法,这步和GetHashCode配合起来才会有效果30
/// </summary>31
/// <param name="o">要检测的对象</param>32
/// <returns>返回是否相同</returns>33
public override bool Equals( object o ) 34
{35
return o.GetHashCode() == myHashCode;36
}37

38
//在这里使用运算符重载,主要是为了进一步演示Equals39
public static bool operator ==(object c1, Class2 c2) 40
{41
return c1.GetHashCode().Equals( c2.GetHashCode() );42
}43
44
//当你重载了==运算符后,必须要重载!=运算符45
public static bool operator !=(object c1, Class2 c2) 46
{47
return c1.GetHashCode().Equals( c2.GetHashCode() );48
}49
50
}51
}这里是测试代码:
1
System.Collections.ArrayList arr = new System.Collections.ArrayList();
2
3
int i = 0;
4
for ( i = 0; i < 4 ; i ++ ) {
5
Class2 class2 = new Class2(i); //我们添加四个对象
6
arr.Add( class2 );
7
}
8
9
for ( i = 0; i < 4 ; i ++ )
10
{
11
Class2 class2 = new Class2(i); //重新创建四个对象,判断是否和容器中的对象相等
12
Console.WriteLine( i + ":" + arr.Contains(class2));//全部输出true
13
}
14
15
16
17
Class2 class2_1 = new Class2(1); //再创建对象1,并添加到容器中,以判断是否有多个对象1存在
18
arr.Add( class2_1 );
19
20
for ( i = 0;i<arr.Count;i++ ) {
21
Class2 class2_3 = new Class2(1);//这个时候我们要检测出容器中有多少个对象1
22
if ( arr[i].Equals( class2_3 ) ) {//我们用Equals来检测是否相等
23
Console.WriteLine( "我用Equals找到\t" );
24
}
25
if ( arr[i] == class2_3 ) //我们用==来检测相等
26
{
27
Console.WriteLine( "我用==找到\t" );
28
}
29
Console.WriteLine( arr[i].ToString() + "\t HashCode:" + arr[i] .GetHashCode()); //这里输出HashCode和ToString()查看
30
31
}
32
System.Collections.ArrayList arr = new System.Collections.ArrayList();2

3
int i = 0;4
for ( i = 0; i < 4 ; i ++ ) {5
Class2 class2 = new Class2(i); //我们添加四个对象6
arr.Add( class2 );7
}8

9
for ( i = 0; i < 4 ; i ++ ) 10
{11
Class2 class2 = new Class2(i); //重新创建四个对象,判断是否和容器中的对象相等12
Console.WriteLine( i + ":" + arr.Contains(class2));//全部输出true13
}14

15

16

17
Class2 class2_1 = new Class2(1); //再创建对象1,并添加到容器中,以判断是否有多个对象1存在18
arr.Add( class2_1 ); 19

20
for ( i = 0;i<arr.Count;i++ ) {21
Class2 class2_3 = new Class2(1);//这个时候我们要检测出容器中有多少个对象122
if ( arr[i].Equals( class2_3 ) ) {//我们用Equals来检测是否相等23
Console.WriteLine( "我用Equals找到\t" );24
}25
if ( arr[i] == class2_3 ) //我们用==来检测相等26
{27
Console.WriteLine( "我用==找到\t" );28
}29
Console.WriteLine( arr[i].ToString() + "\t HashCode:" + arr[i] .GetHashCode()); //这里输出HashCode和ToString()查看30
31
}32

相信上面的代码很容易看的懂..在我的Class2类中覆写了GetHashCode() ,ToString() ,Equals(object o),并重载了==运算符和!=运算符..将传递的id作为HashCode,然后判断当前传递的对象Object.GetHashCode是否等于当前对象的GetHashCode..这样就解决了ArrayList.Contains不能对Class做出正确判断的问题..
另外,还有一个容器Hashtable的使用和判断,并不能用上面的方法解决..因为Hashtable.Contains的实现方法是IDictionary.Contains来做判断..需要实现IDictionary接口的方法才可以.因为牵涉到的内容比较多.所以不能在这里全部写完..关于具体的方法和实现,我会找时间写出来的..
最后大家可以自己做一个没有实现Equals方法的类,再用ArrayList.Contains来做判断..可以看到结果都是flase..和上面的代码是个对比..
这个方法不仅可以用在ArrayList,而且也可以在多个地方使用,比如两个Class之间的关联?Class1和Class2是否关联??
既然需要再次重写Equals方法,今天就用Hashtable的实现,先说一下Hashtable,首先Hashtable保存的是以(键/值)为一对插入的..你可以方便的通过键来查找值,在国外的书中称为字典,他的作用也如其名一样..使用起来很类似字典..你知道一个字母a ,要查找对应字母a的值,可以这么写Hashtable["a"],这样就查到了对应a的值..但是需要注意一点,就是添加到Hashtable不能重复,必须有唯一性.并且因为Hashtable添加/删除是无序的,所以速度要快..就是因为它们是无序的,所以你不能通过索引,类似string[0].string[1]这样的方式访问到Hashtable中的数据.针对这样的情况,适应于下列地方,你知道key,但是value不确定,并且需要频繁的插入和删除操作,而这个时候数组和ArrayList就不是最佳的选择,因为在你获取某对象的时候,需要遍历..而Hashtable则比他们的效率要高..
下面是个简单的例子对ArrayList和Hashtable做了个对比:
1
int i;
2
System.Collections.Hashtable hs = new System.Collections.Hashtable();
3
System.Collections.ArrayList arr = new System.Collections.ArrayList();
4
5
long StartTime = DateTime.Now.Ticks;
6
for ( i = 0 ; i < 10000 ; i ++ ) {
7
arr.Add(i);
8
}
9
long EndTime = DateTime.Now.Ticks;
10
Console.WriteLine( "ArryList添加执行时间:" + (EndTime-StartTime) );
11
12
StartTime = DateTime.Now.Ticks;
13
for ( i = 0 ; i < 10000 ; i ++ )
14
{
15
hs.Add(i,"我是"+i);
16
}
17
EndTime = DateTime.Now.Ticks;
18
Console.WriteLine( "Hashtable添加执行时间:" + (EndTime-StartTime) );
19
20
StartTime = DateTime.Now.Ticks;
21
for ( i = 0 ; i < 10000 ; i ++ )
22
{
23
arr.Contains(i) ;
24
}
25
EndTime = DateTime.Now.Ticks;
26
Console.WriteLine( "ArryList查找执行时间:" + (EndTime-StartTime) );
27
28
StartTime = DateTime.Now.Ticks;
29
for ( i = 0 ; i < 10000 ; i ++ )
30
{
31
hs.Contains(i) ;
32
}
33
EndTime = DateTime.Now.Ticks;
34
Console.WriteLine( "Hashtable查找执行时间:" + (EndTime-StartTime) );
35
36
StartTime = DateTime.Now.Ticks;
37
for ( i = 0 ; i < 10000 ; i ++ )
38
{
39
arr.Remove(i) ;
40
}
41
EndTime = DateTime.Now.Ticks;
42
Console.WriteLine( "ArryList删除执行时间:" + (EndTime-StartTime) );
43
44
StartTime = DateTime.Now.Ticks;
45
for ( i = 0 ; i < 10000 ; i ++ )
46
{
47
hs.Remove(i) ;
48
}
49
EndTime = DateTime.Now.Ticks;
50
Console.WriteLine( "Hashtable删除执行时间:" + (EndTime-StartTime) );
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
上面的代码执行结果:
1
ArryList添加执行时间:0
2
Hashtable添加执行时间:300432
3
ArryList查找执行时间:10214688
4
Hashtable查找执行时间:0
5
ArryList查找执行时间:701008
6
Hashtable查找执行时间:0
2
3
4
5
6
结果可以说明,使用正确的容器对程序的优化是至关重要的..而选择正确的容器,也完全在于对容器的了解和对业务的熟悉..这里需要注意一点,开头的地方说添加也很快,但是Hashtable的添加要比ArrayList慢很多..主要是因为Hashtable要对key做一系列的索引算法,也是为了更好的查找和删除做的准备工作... 另外这里提示一点,就是在Hashtable初始化的时候可以给它指定默认容量和加载因子,其中加载因子越小,则平均查找速度就越快,但内存消耗就越大..默认的容量是16,加载因子是1..加载因子的取值范围是0.1-1之间..可以根据你的数据大小适当的调整容量和加载因子来提高速度..
1
System.Collections.Hashtable hs = new System.Collections.Hashtable();
2
int i;
3
long StartTime = DateTime.Now.Ticks;
4
for ( i = 0; i < 10000;i ++ )
5
{
6
hs.Add( i,"我是"+i );
7
}
8
long EndTime = DateTime.Now.Ticks;
9
Console.WriteLine( "hs添加执行时间:" + (EndTime-StartTime) );
10
11
System.Collections.Hashtable hs2 = new System.Collections.Hashtable(10001,0.3f);
12
StartTime = DateTime.Now.Ticks;
13
for ( i = 0; i < 10000;i ++ )
14
{
15
hs2.Add( i,"我是"+i );
16
}
17
EndTime = DateTime.Now.Ticks;
18
Console.WriteLine( "hs2添加执行时间:" + (EndTime-StartTime) );
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
上面的代码执行结果:
1
hs添加执行时间:200288
2
hs2添加执行时间:100188
这个结果,经过我测试,在你i<40000的时候,是明显的,但是i>40000的时候,效率就很差了.甚至是不如默认的...并且根据数据的不同,这个结果都会有偏差,不过数据量保持在某个值(这个值也是很变化的)以下的时候,调整加载因子比默认的速度还是要快点的... 2
如果你传递给Hashtable的key是Class,而如何保证key的唯一呢??因为Hashtable还是用到了HashCode做为验证唯一性,所以你还要在你的类覆写GetHashCode(),并且也要重写Equals()以判断是否相等..具体问题可以看给你的类重写Equals--检测Class是否相等 .基础的东西这里都有说明..尤其是后面的回复..可以多看看..
现在来看Hashtable的具体实现,首先你添加一对键/值后,会将键和对象的引用放入存储桶,Hashtable的键索引是按key对象的GetHashCode方法计算的,查找的时候也是用查找的键与存储的键对比,对比的方法还是要依赖Equals!!这个过程也说明了,为什么覆写了GetHashCode之后也要覆盖Equals了..看代码:
1
//============= 用户类 ==================
2
public class User
3
{
4
private string name = null;
5
public User(string name)
6
{
7
this.name = name;
8
}
9
10
public override string ToString() //覆写ToString()
11
{
12
return name;
13
}
14
15
//这里因为name是不重复的,所以获取name的HashCode,作为
16
public override int GetHashCode()
17
{
18
return ToString().GetHashCode();
19
}
20
21
/// <summary>
22
/// 覆写Equals,判断对象是否具有相同的
23
/// </summary>
24
/// <param name="obj"></param>
25
/// <returns></returns>
26
public override bool Equals(object obj)
27
{
28
User u = obj as User;
29
if ( u == null ) //如果不能转换为当前Class,则返回false
30
return false;
31
if ( this.name == u.name && this.ToString() == u.ToString() )
32
return true;
33
34
return false;
35
}
36
37
}
38
39
// ============ 对应用户的信息 ==============
40
public class UserInfo
41
{
42
private User u;
43
public UserInfo(User us)
44
{
45
this.u = us;
46
}
47
48
public override string ToString()
49
{
50
return u.ToString() + "的信息";
51
}
52
53
}
54
55
//========== 测试代码 Main ==================
56
static void Main(string[] args)
57
{
58
User u1 = new User("小李");
59
User u2 = new User("小王");
60
UserInfo info1 = new UserInfo(u1);
61
UserInfo info2 = new UserInfo(u2);
62
System.Collections.Hashtable hs = new System.Collections.Hashtable();
63
64
hs.Add( u1,info1 );
65
hs.Add( u2,info2 );
66
67
User u3 = new User("小李");
68
UserInfo info3 = new UserInfo(u1);
69
User u4 = new User("小王");
70
71
Console.WriteLine( hs[u3] );
72
Console.WriteLine( hs[u4] );
73
try {
74
hs.Add( u3, info3);
75
}catch(ArgumentException ee) {
76
Console.WriteLine( ee.Message );
77
}
78
79
}
//============= 用户类 ==================2
public class User3
{4
private string name = null;5
public User(string name)6
{7
this.name = name;8
}9
10
public override string ToString() //覆写ToString()11
{12
return name;13
}14
15
//这里因为name是不重复的,所以获取name的HashCode,作为16
public override int GetHashCode() 17
{18
return ToString().GetHashCode();19
}20
21
/// <summary>22
/// 覆写Equals,判断对象是否具有相同的23
/// </summary>24
/// <param name="obj"></param>25
/// <returns></returns>26
public override bool Equals(object obj)27
{28
User u = obj as User; 29
if ( u == null ) //如果不能转换为当前Class,则返回false30
return false;31
if ( this.name == u.name && this.ToString() == u.ToString() )32
return true;33
34
return false;35
}36
37
}38

39
// ============ 对应用户的信息 ==============40
public class UserInfo41
{42
private User u;43
public UserInfo(User us)44
{45
this.u = us;46
}47
48
public override string ToString()49
{50
return u.ToString() + "的信息";51
}52
53
}54

55
//========== 测试代码 Main ==================56
static void Main(string[] args)57
{58
User u1 = new User("小李");59
User u2 = new User("小王");60
UserInfo info1 = new UserInfo(u1);61
UserInfo info2 = new UserInfo(u2);62
System.Collections.Hashtable hs = new System.Collections.Hashtable();63
64
hs.Add( u1,info1 );65
hs.Add( u2,info2 );66
67
User u3 = new User("小李");68
UserInfo info3 = new UserInfo(u1);69
User u4 = new User("小王");70
71
Console.WriteLine( hs[u3] );72
Console.WriteLine( hs[u4] );73
try {74
hs.Add( u3, info3);75
}catch(ArgumentException ee) {76
Console.WriteLine( ee.Message );77
}78
79
}上面的代码实例化了四个类,其中两个小王,两个小李,先添加小李1和小王1到Hashtable中,然后用新的小王2和小李2进行查找Hashtable,因为作为键的User类重写了GetHashCode和Equals,并且实例的时候也都是用的小王和小李,并且他们的string.GetHashCode是一样的..所以会被找到...最后程序又试图添加小李2到Hashtable中,但是因为里面已经存在了一个相同的key.GetHashCode,所以添加导致了异常...上面的输出结果是:
1
小李的信息
2
小王的信息
3
已添加项。字典中的键: “小李” 所添加的键: “小李”
小李的信息2
小王的信息3
已添加项。字典中的键: “小李” 所添加的键: “小李”如果对上面的解释不清楚,参考前两天的两个文章..
StringBuilder的实现与技巧
给你的类重写Equals--检测Class是否相等



浙公网安备 33010602011771号