`

HashCode学习

    博客分类:
  • java
阅读更多


本文转自:http://hi.baidu.com/lkdlhw_2000/blog/item/c28a044a7ddeab2208f7ef41.html


以下内容参见了如下网址:

1、http://blog.csdn.net/ngqzmjmj/archive/2005/04/27/365149.aspx


2、http://blog.csdn.net/pbnow/archive/2006/04/25/677253.aspx


3、http://blog.csdn.net/petercheng456/archive/2005/08/02/444427.aspx



讲到Hashtable和HashMap的区别的时候,就不得不说说hashCode的问题,下面是自己从网上找到的几篇资料,自己结合HashTable来整理了一下:



1、任何class如果覆写了equals()方法,就必须覆写hashCode()。

这样作的目的就是为了你的类就能够很好的与java的集合框架协同工作。如果我们能够确认我们定义的类不会和java集合类产生关系,那么我们完全没有必要在覆写equals()方法的时候覆写hashCode。


2、关于Hashtable,判断key是否相同的条件是:hashCode()相同 &&
满足equals()。这两个方法都是可以重构的,所以Hashtable没有用==,给了程序员实现自己的Hashtable的好的途径,的确Sun想
得很严密。



例子1:

      class a{

            private int b; 

            public a(int c){//构造函数

             b=c;

          }

      public String eqals(Object o){

       if(this==o) return true;

        if(o instanceof a){

         return a.b==(a)o.b;

        }

        return false;

              }

      public static void main(String[] args){

       Map p=new HashMap();

       p.put(new a(1));  

       p.get(new a(1))

     
/*这里返回为null,因为map是根据equals()和hashCode()来判断对象是否相等,所以在类里覆写了equals(),就一定要覆写
hashCode()。关于Hashtable,判断key是否相同的条件是:hashCode()相同 &&
满足equals(),而一个类如果没有覆写equals()方法,那么这个类的equals方法比较的是对象的内存地址。如果没有覆写
HashCode,那么该类的hashCode是通过对象的内存地址转换而来.

       */

         }

       }

       例子二:

       /**

* 类:TestStudent

*/

        public class TestStudent {

         private int age;

         public TestStudent(int age) {

             this.age = age;

         }



         public TestStudent() {

         }



         public boolean equals(Object o) {

//            if (this == o) return true;

//            if (o == null || getClass() != o.getClass()) return false;

//

//            final TestStudent that = (TestStudent) o;

//

//            if (age != that.age) return false;



             return true;

         }



         public int hashCode() {

             return age;

         }

}

/**

* 类:TestTeacher

*/

public class TestTeacher {

         private int age;



         public TestTeacher(int age) {

             this.age = age;

         }



         public TestTeacher() {

         }



         public boolean equals(Object o) {

//            if (this == o) return true;

//            if (o == null || getClass() != o.getClass()) return false;

//

//            final TestTeacher that = (TestTeacher) o;

//

//            if (age != that.age) return false;

//

//            return true;

             return true;

         }



         public int hashCode() {

             return age;

         }

}



public class TestHashtable2 {

         public static void main(String[] args){

             Hashtable table=new Hashtable();

             table.put(new TestTeacher(1),"aaa");

             System.out.print("=========="+table.get(new TestStudent(1)));

         }

}

输出结果是:==========aaa

从上面这个例子就可以看出来Hashtable中key和对象所属的class无关,只和equals方法和hashCode方法的有关。当然正常情况下我们覆写equals方法的时候下面两句话是必不可少的。

if (this == o) return true;//保证了统一对对象的绝对一致性。

if (o == null || getClass() != o.getClass()) return false;

final TestStudent that = (TestStudent) o;//保证了只有同一个类的不同对象进行equals,虽然上面的例子可以进行equals对比不同的对象,但是实际情况中不同对象间的equals进行对比意义不大。

if (age != that.age){

     return false;

}


3、在程序执行期间,同一个对象调用hashCode()必须返回同一个值(同一个应用执行期)


4、如果两个对象equals,那么他们的hashCode()必须相等。(个人感觉这个要求也是为了和java的结合框架协同工作,因为我们自己定义的
类完全可以覆写equals方法,但是不覆写hashCode,尽管我们可以这样做,但是这样做是不合理的,他会为我们的程序埋下错误隐患。虽然java
没有从在编译器对上面的说法进行控制,但是上面的说法是我们在设计类的时候应该遵循的规则)。


5、如果两个对象equals不相等,那么他们的hashCode()不必产生不同的结果(上面的例2就可以说明问题),程序员应该注意到,对不同的对象产生不同的hashCode(),有可能提升 hash table(哈希表)的效率


7、Map.put(key,value)时根据key.hashCode生成一个内部hash值,根据这个hash值将对象存放在一个table中。
Map.get(key)会比较key.hashCode和equals方法,当且仅当这两者相等时,才能正确定位到table;java中Set是通过
Map实现的,所以Map和Set的所有实现类都要注意这一点.


8、

StringBuffer buffer = new StringBuffer();

buffer.append("some");

String a = buffer.toString();

String b = "some";



现在有三个式子,结果在后面用注释的形式标出:



a.equals(b);//true

a==b;//false

a.hashCode()==b.hashCode();//true



说明:"ab"=="ab"是因为编译的时候就把这两个指向同一个常量了,属于编译优化的一部分。但是动态生成的字符串的地址就不一样了。比如
String a = buffer.toString(), 但是buffer.append("some")和String b =
"some"里两个"some"常量应当是指向同一个地址。



9、如果想判断是否两个对象引用指向同一个实体,唯一的正确途径是使用==,因为只有它不会被重构,要慎用hashCode


10、有一点我们应该是能够确认的,就是同样的数字转换成hashCode后的值肯定一样,因此我们在覆写hashCode时候应该利用自己编写的
类独特属性进行hashCode运算(当然不仅仅是一个属性,可以是多个属性),这样我们就可以尽量的保证每个类对象的具有不同的hashCode,如果
这些不同的对象有机会进入同一个map的时候就可以保证其hashCode不一样,从而提高map的检索。(下面文章中提到了,在一个map中的对象应该
尽量的避免其hashCode一样)。


11、关于hashCode的常规写法:把所有属性都参与散列,通常的算法就是将不同的质数与字段做乘积和,比如一个类具有两个属性age和
name,那么return  
11*age+13*name;。至于为什么用质数的问题个人就没有深入研究了。对于我们对算法要求不是很高的我们当然可以让所有属性都参与散列,但是如
果我们为了追求一点效率,那么我觉的应该只将类的独特字段进行散列就可以了。


12、另外一点就是充分利用IDE,刚开始看这个问题的时候,只知道覆写了equals就应该覆写hashCode,但是hashCode该怎么
写,自己也不是很清楚,通过自己的IDE,Intellij
IDEA自动生成的hashCode,就了解到了将类的所有属性都参与散列的方法,个人觉的,IDE给出的算法虽然不是针对某个类最优的算法,但是对于我
们一般的应用程序,对算法要求不高的程序,还是很有参考价值的。


以下内容是转帖http://dev2dev.bea.com.cn/bbsdoc/20060807307.html



为什么HashCode对于对象是如此的重要?


  一个对象的HashCode就是一个简单的Hash算法的实现,虽然它和那些真正的复杂的Hash算法相比还不能叫真正的算法,它如何实现它,
不仅仅是程序员的编程水平问题,而是关系到你的对象在存取是性能的非常重要的关系.有可能,不同的HashCode可能会使你的对象存取产生,成百上千倍
的性能差别。


  我们先来看一下,在JAVA中两个重要的数据结构:HashMap和Hashtable,虽然它们有很大的区别,如继承关系不同,对value的约束条件(是否允许null)不同,以及线程安全性等有着特定的区别,
但从实现原理上来说,它们是一致的.所以,我们只以Hashtable来说明:


  在java中,存取数据的性能,一般来说当然是首推数组,但是在数据量稍大的容器选择中,Hashtable将有比数组性能更高的查询速度.具体原因看下面的内容。


  Hashtable在存储数据时,一般先将作为key的对象的HashCode和0x7FFFFFFF做与操作,因为一个对象的
HashCode可以为负数,这样操作后可以保证它为一个正整数.然后以Hashtable的长度取模,得到值对象在Hashtable中的索引。


  index = (o.hashCode() &
0x7FFFFFFF)%hs.length;这个值对象就会直接放在Hashtable的第index位置,对于写入,这和数组一样,把一个对象放在其
中的第index位置,但如果是查询,经过同样的算法,Hashtable可以直接通过key得到index,从第index取得这个值对象,而数组却要
做循环比较.所以对于数据量稍大时,Hashtable的查询比数据具有更高的性能。


  虽然不同对象有不同的hashcode,但不同的hashCode经过与长度的取余,就很可能产生相同的index。


  极端情况下会有大量的对象产生一个相同的索引.这就是关系Hashtable性能问题的最重要的问题:


  Hash冲突。


  常见的Hash冲突是不同key对象最终产生了相同的索引,而一种非常甚至绝对少见的Hash冲突是,如果一组对象的个数大过了int范围,而
HashCode的长度只能在int范围中,所以肯定要有同一组的元素有相同的HashCode,这样无论如何他们都会有相同的索引.当然这种极端的情况
是极少见的,可以暂不考虑,但是对于同的HashCode经过取模,则会产中相同的索引,或者不同的对象却具有相同的HashCode,当然具有相同的索
引。


  事实上一个设计各好的HashTable,一般来说会比较平均地分布每个元素,因为Hashtable的长度总是比实际元素的个数按一定比例进
行自增(装填因子一般为0.75)左右,这样大多数的索引位置只有一个对象,而很少的位置会有几个元素.所以Hashtable中的每个位置存放的是一个
链表,对于只有一个对象是位置,链表只有一个首节点(Entry),Entry的next为null.然后有hashCode,key,value属性保
存了该位置的对象的HashCode,key和value(对象本身),如果有相同索引的对象进来则会进入链表的下一个节点.如果同一个索引中有多个对
象,根据HashCode和key可以在该链表中找到一个和查询的key相匹配的对象。


  从上面我看可以看到,对于HashMap和Hashtable的存取性能有重大影响的首先是应该使该数据结构中的元素尽量大可能具有不同的HashCode
,虽然这并不能保证不同的HashCode产生不同的index,但相同的HashCode一定产生相同的index,从而影响产生Hash冲突。


  对于一个象,如果具有很多属性,把所有属性都参与散列,显然是一种笨拙的设计.因为对象的HashCode()方法几乎无所不在地被自动调用,如equals比较,如果太多的对象参与了散列.那么需要的操作常数时间将会增加很大.
所以,挑选哪些属性参与散列绝对是一个编程水平的问题。


  从实现来说,一般的HashCode方法会这样:


  return Attribute1.HashCode() + Attribute1.HashCode()..[+super.HashCode()]。


  我们知道,每次调用这个方法,都要重新对方法内的参与散列的对象重新计算一次它们的
HashCode的运算,如果一个对象的属性没有改变,仍然要每次都进行计算,所以如果设置一个标记来缓存当前的散列码,只要当参与散列的对象改变时才重
新计算,否则调用缓存的hashCode,这可以从很大程度上提高性能



  默认的实现是将对象内部地址转化为整数作为HashCode,这当然能保证每个对象具有不同的HasCode
,因为不同的对象内部地址肯定不同(废话),但java语言并不能让程序员获取对象内部地址,所以,让每个对象产生不同的HashCode有着很多可研究的技术。


  如果从多个属性中采样出能具有平均分布的hashCode的属性,这是一个性能和多样性相矛盾的地方,如果所有属性都参与散列,当然
hashCode的多样性将大大提高,但牺牲了性能,而如果只能少量的属性采样散列,极端情况会产生大量的散列冲突,如对"人"的属性中,如果用性别而不
是姓名或出生日期,那将只有两个或几个可选的hashcode值,将产生一半以上的散列冲突.所以如果可能的条件下,专门产生一个序列用来生成
HashCode将是一个好的选择(当然产生序列的性能要比所有属性参与散列的性能高的情况下才行,否则还不如直接用所有属性散列)。


  如何对HashCode的性能和多样性求得一个平衡,可以参考相关算法设计的书,其实并不一定要求非常的优秀,只要能尽最大可能减少散列值的聚集.重要的是我们应该记得HashCode对于我们的程序性能有着生要的影响,在程序设计时应该时时加以注意。







什么是 Hash




Hash,一般翻译做“散列”,也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射,
pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不
同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。

数学表述为:h = H(M) ,其中H( )--单向散列函数,M--任意长度明文,h--固定长度散列值。

在信息安全领域中应用的Hash算法,还需要满足其他关键特性:



一当然是单向性(one-way),从预映射,能够简单迅速的得到散列值,而在计算上不可能构造一个预映射,使其散列结果等于某个特定的散列值,即构造相
应的M=H-1(h)不可行。这样,散列值就能在统计上唯一的表征输入值,因此,密码学上的 Hash 又被称为"消息摘要(message
digest)",就是要求能方便的将"消息"进行"摘要",但在"摘要"中无法得到比"摘要"本身更多的关于"消息"的信息。


第二是
抗冲突性(collision-resistant),即在统计上无法产生2个散列值相同的预映射。给定M,计算上无法找到M',满足
H(M)=H(M') ,此谓弱抗冲突性;计算上也难以寻找一对任意的M和M',使满足H(M)=H(M')
,此谓强抗冲突性。要求"强抗冲突性"主要是为了防范所谓"生日攻击(birthday
attack)",在一个10人的团体中,你能找到和你生日相同的人的概率是2.4%,而在同一团体中,有2人生日相同的概率是11.7%。类似的,当预
映射的空间很大的情况下,算法必须有足够的强度来保证不能轻易找到"相同生日"的人。


第三是映射分布均匀性和差分分布均匀性,散列结果
中,为 0 的 bit 和为 1 的 bit ,其总数应该大致相等;输入中一个 bit 的变化,散列结果中将有一半以上的 bit
改变,这又叫做"雪崩效应(avalanche effect)";要实现使散列结果中出现 1bit 的变化,则输入中至少有一半以上的 bit
必须发生变化。其实质是必须使输入中每一个 bit 的信息,尽量均匀的反映到输出的每一个 bit 上去;输出中的每一个
bit,都是输入中尽可能多 bit 的信息一起作用的结果。

 

分享到:
评论
1 楼 George_ghc 2012-01-18  
过来学习一下!

相关推荐

    Java基础加强_ArrayList_HashSet的比较及Hashcode分析

    在Java编程语言中,ArrayList和HashSet是两种常用的集合类,它们各自有其特性和应用场景。在实际开发中,理解它们的差异以及如何有效地利用它们是非常...学习和掌握这些基础知识,将有助于成为一名更优秀的Java开发者。

    学习Object类——为什么要重写equeals和hashcode方法

    Object 类的 equals 和 hashCode 方法的重要性与实现 在 Java 编程语言中,Object 类是所有类的父类,但是在实际开发中,我们往往需要重写 Object 中的 equals 和 hashCode 方法,以便正确地比较对象的逻辑内容,而...

    hashcode2018:提交Hashcode 2018

    通过这些文件,我们可以回顾当时的策略,学习如何运用哈希码和Python来解决实际问题。代码分析可以帮助我们理解如何设计高效算法,如何利用Python的特性来优化性能,以及如何通过团队协作来共同解决问题。 总的来说...

    hashcode2021:源HashCode 2021

    通过参加源HashCode 2021活动或学习相关资料,开发者可以提升对Java哈希码的理解,优化自己的代码,提高程序的运行效率。在实际开发过程中,合理运用哈希码能极大地改善程序性能,尤其是在处理大量数据时。

    equals-hashcode-processor-1.0.0.zip

    《深入理解Scala Futures的异步重试机制》 在当今的并发编程中,Scala的Futures扮演着重要的角色。Futures允许我们以非阻塞的方式处理...通过深入学习并运用这个库,开发者可以构建出更健壮、更易于维护的异步系统。

    Java的Object类讲解案例代码 equals()、hashCode()、finalize()、clone()、wait()

    通过该案例代码,你可以学习如何在自己的类中正确重写equals()、hashCode()、toString()等方法,提高代码质量和可读性。 经验丰富的Java开发者:即使你已经有一定的Java开发经验,仍然值得深入了解Object类的使用。...

    hashcode

    通过学习和分析这些示例,开发者可以更好地理解如何在实际项目中适当地使用和实现`hashCode()`。 在实际开发中,`hashCode()` 的实现可以基于对象的属性来计算。通常,我们会将对象的关键属性进行位运算,如异或或...

    深入理解equals和hashCode方法

    equals和hashCode方法是Java中Object类提供的两个重要方法,对以后的学习有很大的帮助。本文将深入剖析这两个方法,帮助读者更好地理解和使用它们。 equals方法 equals方法是用于比较两个对象是否相同的方法。它是...

    hashcode-2k21:我们团队的HashCode 2k21解决方案的仓库

    【哈希码(HashCode)2k21】是Google举办的一项年度编程挑战赛,HashCode旨在让参赛者通过解决实际的大型...通过深入研究这个仓库,我们可以学习到如何面对复杂的编程挑战,以及如何在团队环境中高效地协作和解决问题。

    INF580_HashCode2014:用 INF580 的知识解决 Google HashCode2014!

    在IT领域,编程竞赛如Google HashCode已经成为程序员提升技能、展示才华的重要舞台...通过这样的比赛,参与者不仅可以提高编程技巧,还能学习到如何在团队中协作,共同解决复杂问题,这些都是IT行业极其重要的软技能。

    google-hashcode:Paprika团队为Google Hashcode 2017和2018建议的解决方案。与@ AJRamos308,@ EdgarACarneiro和@therealdelay合作

    标题中的“google-hashcode”指的是Google举办的一项名为Hash Code的编程竞赛,这是一个团队编程挑战,参赛者需要解决实际的工程问题。Paprika团队在2017年和2018年的比赛中提供了他们的解决方案。从描述来看,这个...

    128-hashcode-activity

    标题 "128-hashcode-activity" 暗示了我们正在探讨一个与哈希码相关的编程主题,可能是一个程序或代码片段,它...通过研究这个项目,他们可以学习到如何设计和实现高效的哈希函数,以及如何在实际问题中应用这些知识。

    java se1 学习

    Java SE1 学习主要涉及Java编程语言的基础概念和核心特性。Java是一种广泛使用的面向对象的编程语言,它的设计目标是具有平台独立性、高效性和安全性。在这个学习阶段,我们将重点探讨Object类和String类。 Object...

    《SCJP学习指南》chap7

    ### 《SCJP学习指南》chap7:泛型与集合 #### 标题与描述解析 本章节聚焦于Java中的两个关键概念:泛型(Generics)与集合(Collections),这是Java 5引入的重要特性之一。通过深入研究这些概念,读者能够更好地...

    hashcode-2021练习

    "hashCode-2021练习"是一个编程挑战项目,它涉及到使用TypeScript语言来解决算法问题。...通过这个练习,你不仅可以提升TypeScript编程技能,还能锻炼解决问题的能力,学习如何将理论知识应用到实际挑战中。

    kotlin学习视频.txt

    - 数据类:自动实现`equals()`, `hashCode()`, `toString()`等方法。 - 密封类:提供有限数量的子类,通常用于表示一组封闭的状态或选项。 #### 3. Android开发中的Kotlin应用 - 使用Kotlin进行UI布局的创建和管理...

    5_hash5_hashcod

    标题中的"5_hash5_hashcod"以及描述中的"5_hashcod5_hashcod"似乎存在一些复制错误,看起来它们想要强调的是“hashCode”这个概念。...通过学习和实践,开发者可以更好地掌握`hashCode`的用法,从而优化他们的代码。

    本人kotlin学习资料

    - **数据类**:用于数据封装,自动提供equals()、hashCode()和toString()等方法。 - **接口和继承**:理解Kotlin的单一继承和多接口实现机制。 - **协程**:Kotlin的协程机制可以方便地处理异步操作,避免回调地狱,...

    URLDNS反序列化链学习.doc

    【URLDNS反序列化链学习】文档主要涉及的是Java中的一个安全问题,即URLDNS反序列化链。这是一种利用Java对象序列化机制进行攻击的技术,通常与代码注入和远程代码执行(RCE)漏洞相关。在这个特定的案例中,虽然不能...

    java面试题 值得学习

    3. `hashCode()`提供对象的哈希码,`equals()`相等的物体`hashCode()`必须相等,但`hashCode()`相等的物体`equals()`不一定相等。 在面试或实际编程中,理解这些基本概念对于编写正确和高效代码至关重要。对于Java...

Global site tag (gtag.js) - Google Analytics