博弈Java讲义 - 关于equals & hash_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 博弈Java讲义 - 关于equals & hash

博弈Java讲义 - 关于equals & hash

 2014/11/30 21:26:19  boyitech  程序员俱乐部  我要评论(0)
  • 摘要:覆盖equals方法和hashCode方法看似简单,但其实不然,如果没有按照jdk的通用规范去覆盖,那么基于这些约定的类将可能无法正常工作,例如基于散列的集合类HashMap和HashSet.对于值类,我们通常需要覆盖Object.equals方法,因为我们希望通过equals方法知道它们在逻辑上是否相等.相应的这个类的实例可以被用作map的key,或者set的元素的时候才会表现出预期的行为.对于"值类",枚举是个例外,因为枚举的每个值都是个单例.在覆盖equals时
  • 标签:has Hash Java 博弈

覆盖equals方法和hashCode方法看似简单,但其实不然,如果没有按照jdk的通用规范去覆盖,那么基于这些约定的类将可能无法正常工作,例如基于散列的集合类HashMap和HashSet.class="Apple-converted-space">?
? 对于值类,我们通常需要覆盖Object.equals方法,因为我们希望通过equals方法知道它们在逻辑上是否相等.相应的这个类的实例可以被用作map的key,或者set的元素的时候才会表现出预期的行为. 对于"值类",枚举是个例外,因为枚举的每个值都是个单例.?
? 在覆盖equals时,必须遵守JavaSE Object的规范:自反性(reflective), 对称性 (symmetric),传递性(transitive),一致性(consistent),对于任何非null的引用x, x.equals(null)返回false. 对于每个覆盖类equals的类,都应该有相应的单元测试去检查是否有没有违反上述约定。下面是个简单的例子?

Java代码?
  1. public?class?PhoneNumber?{??
  2. ??
  3. ????private?int?countryCode;??
  4. ????private?String?nationalNumber;??
  5. ??????
  6. ????public?PhoneNumber(){??
  7. ????????super();??
  8. ????}??
  9. ??????
  10. ????public?PhoneNumber(int?countryCode,?String?nationalNumber)?{??
  11. ????????super();??
  12. ????????this.countryCode?=?countryCode;??
  13. ????????this.nationalNumber?=?nationalNumber;??
  14. ????}??
  15. ????/**?
  16. ?????*?@return?the?countryCode?
  17. ?????*/??
  18. ????public?int?getCountryCode()?{??
  19. ????????return?countryCode;??
  20. ????}??
  21. ????/**?
  22. ?????*?@param?countryCode?the?countryCode?to?set?
  23. ?????*/??
  24. ????public?void?setCountryCode(int?countryCode)?{??
  25. ????????this.countryCode?=?countryCode;??
  26. ????}??
  27. ??????
  28. ????/**?
  29. ?????*?@return?the?nationalNumber?
  30. ?????*/??
  31. ????public?String?getNationalNumber()?{??
  32. ????????return?nationalNumber;??
  33. ????}??
  34. ????/**?
  35. ?????*?@param?nationalNumber?the?nationalNumber?to?set?
  36. ?????*/??
  37. ????public?void?setNationalNumber(String?nationalNumber)?{??
  38. ????????this.nationalNumber?=?nationalNumber;??
  39. ????}??
  40. ????@Override??
  41. ????public?boolean?equals(Object?o){??
  42. ????????if(this?==?o){??
  43. ????????????return?true;??
  44. ????????}??
  45. ????????if(!(o?instanceof?PhoneNumber)){??
  46. ????????????return?false;??
  47. ????????}??
  48. ????????PhoneNumber?pn?=?(PhoneNumber)?o;??
  49. ????????return?this.countryCode?==?pn.getCountryCode()??
  50. ????????????????&&?(?this.nationalNumber?==?null????pn.nationalNumber?==?null?:?this.nationalNumber.equals(pn.nationalNumber));??
  51. ????}?????
  52. }??
  53. ??
  54. import?static?org.junit.Assert.*;??
  55. ??
  56. import?org.junit.Test;??
  57. ??
  58. public?class?PhoneNumberTest?{??
  59. ??
  60. ????@Test??
  61. ????public?void?testEqualsReflexive(){??
  62. ????????PhoneNumber?pn1?=?new?PhoneNumber(86,?"12345");??
  63. ????????assertTrue(pn1.equals(pn1));??
  64. ????????PhoneNumber?pn2?=?new?PhoneNumber();??
  65. ????????assertTrue(pn2.equals(pn2));??
  66. ????}??
  67. ??????
  68. ????@Test??
  69. ????public?void?testEqualsSymmetric(){??
  70. ????????PhoneNumber?pn1?=?new?PhoneNumber(86,?"12345");??
  71. ????????PhoneNumber?pn2?=?new?PhoneNumber(86,?"12345");??
  72. ????????assertEquals(pn1.equals(pn2),?pn2.equals(pn1));??
  73. ????}??
  74. ??????
  75. ??????
  76. ????@Test??
  77. ????public?void?testEqualsTransitive(){??
  78. ????????PhoneNumber?pn1?=?new?PhoneNumber(86,?"12345");??
  79. ????????PhoneNumber?pn2?=?new?PhoneNumber(86,?"12345");??
  80. ????????PhoneNumber?pn3?=?new?PhoneNumber(86,?new?String("12345"));??
  81. ????????assertTrue(pn1.equals(pn2));??
  82. ????????assertTrue(pn2.equals(pn3));??
  83. ????????assertTrue(pn1.equals(pn3));??
  84. ????}??
  85. ??????
  86. ????@Test??
  87. ????public?void?testEqualsConsistent(){??
  88. ????????PhoneNumber?pn1?=?new?PhoneNumber(86,?"12345");??
  89. ????????PhoneNumber?pn2?=?new?PhoneNumber(86,?"12345");??
  90. ????????for(int?i=0;?i<10?;?i++){??
  91. ????????????assertTrue(pn1.equals(pn2));??
  92. ????????}??
  93. ????}??
  94. ??????
  95. ????@Test??
  96. ????public?void?testEqualsWithNull(){??
  97. ????????PhoneNumber?pn1?=?new?PhoneNumber(86,?"12345");??
  98. ????????assertFalse(pn1.equals(null));??
  99. ????}??
  100. ??????
  101. }??



当然还有一些实现高质量equals方法的诀窍:?
?? 1. 使用==caozuofu.html" target="_blank">操作符检查"参数是否为正确的引用"?
?? 2. 使用instanceof检查类型?
?? 3. 把参数转化为正确的类型?
?? 4. 选择逻辑比较的关键域,注意比较的顺序,primitive的比较可以放在前面,或者最有可能不一致性的域?
?? 5. 如果有double,float类型,用Double.compare,Float.compare比较?
?? 6. 覆盖equals重要覆盖hashCode

?

如前文所述,在覆盖了equals方法的类中,也必须覆盖hashCode方法。否则违反了Object.hashCode的通用约定会导致该类无法和基于散列的集合(HashMap,HashSet和HashTable)一起正常使用?
? 如下约定内容摘自Object规范:?
? 1. 在应用程序中,只要对象的euqals方法的比较操作所用的信息没有修改,那么对于同一个对象的调用多次hashCode,必须始终如一返回同一个哈希值。?
? 2. 如果两个对象通过equals比较相等,那么它们的哈希值相同。?
? 3. 如果两个对象通过euqals比较不等,他们的哈希值可能相同,取决于hashCode的实现,由此散列表的性能也会有区别。?
? 以前面的PhoneNumber类为例,编写了如下的测试用例:?
??
?

Java代码?
  1. @Test??
  2. ??public?void?testHashCode(){??
  3. ????PhoneNumber?pn1?=?new?PhoneNumber(86,?"12345");??
  4. ????PhoneNumber?pn2?=?new?PhoneNumber(86,?"12345");??
  5. ????Map<PhoneNumber,String>?map?=?new?HashMap<PhoneNumber,String>();??
  6. ????map.put(pn1,?"12345");??
  7. ????assertNotNull(map.get(pn2));??
  8. ??}??



? 发现测试失败了,但是两个对象通过equals比较是相等的。由于并没有覆盖hashCode方法导致两个相等的对象不能获得相同的散列码。根据约定重写hashCode:?

Java代码?
  1. @Override??
  2. public?int?hashCode(){??
  3. ????int?result?=?17;??
  4. ????result?=?31?*?result?+?countryCode;??
  5. ????if(nationalNumber?!=?null)??
  6. ????result?=?31?*?result?+?nationalNumber.hashCode();??
  7. ????return?result;??
  8. }??


??
? 好的散列函数通常倾向于"为不相等的对象产生不相等的散列码", 否则会引起冲突,使散列表想链表退化。计算是可以把冗余的域排除在外。注意不要试图从散列码计算中排除关键域来提高性能。?

发表评论
用户名: 匿名