文档章节

HashCode和hashMap、hashTable

ForingY
 ForingY
发布于 2016/03/02 13:09
字数 2367
阅读 75
收藏 4
点赞 1
评论 0

什么是哈希码(HashCode)

在Java中,哈希码代表对象的特征。

例如对象 String str1 = “aa”, str1.hashCode= 3104

String str2 = “bb”, str2.hashCode= 3106

String str3 = “aa”, str3.hashCode= 3104

根据HashCode由此可得出str1!=str2,str1==str3

下面给出几个常用的哈希码的算法。

1:Object类的hashCode.返回对象的内存地址经过处理后的结构,由于每个对象的内存地址都不一样,所以哈希码也不一样。

2:String类的hashCode.根据String类包含的字符串的内容,根据一种特殊算法返回哈希码,只要字符串所在的堆空间相同,返回的哈希码也相同。

3:Integer类,返回的哈希码就是Integer对象里所包含的那个整数的数值,例如Integer i1=new Integer(100),i1.hashCode的值就是100 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样。


HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键。  

那么Java运行时环境是如何判断HashSet中相同对象、HashMap中相同键的呢?当存储了“相同的东西”之后Java运行时环境又将如何来维护呢?   

在研究这个问题之前,首先说明一下JDK对equals(Object obj)和hashcode()这两个方法的定义和规范:  

在Java中任何一个对象都具备equals(Object obj)和hashcode()这两个方法,因为他们是在Object类中定义的。  

equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。  

hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。  

接下来有两个个关于这两个方法的重要规范(我只是抽取了最重要的两个,其实不止两个): 

 规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该 相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。 

 规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode可能相同”。  

根据这两个规范,可以得到如下推论:  

1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。 

 2、如果两个对象不equals,他们的hashcode有可能相等。  

3、如果两个对象hashcode相等,他们不一定equals。  

4、如果两个对象hashcode不相等,他们一定不equals。   

这样我们就可以推断Java运行时环境是怎样判断HashSet和HastMap中的两个对象相同或不同了。我的推断是:先判断hashcode是否相等,再判断是否equals。

测试程序如下:首先我们定义一个类,重写hashCode()和equals(Object obj)方法 

 class A {           
    @Override     
    public boolean equals(Object obj) {
                 System.out.println("判断equals"); 
                 return false;         
     }           
    @Override     
     public int hashCode() {     
        System.out.println("判断hashcode");     
                 return 1;          
        }     
      }

然后写一个测试类,代码如下:

public class Test {           
     public static void main(String[] args) {     
         Map<A,Object> map = new HashMap<A, Object>();
         map.put(new A(), new Object());
         map.put(new A(), new Object());              
         System.out.println(map.size());
      }
}

运行之后打印结果是:   

判断hashcode 

判断hashcode  

判断equals 


HashCode的作用

首先,想要明白hashCode的作用,你必须要先知道Java中的集合。
  总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。你知道它们的区别吗?前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
    于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。如果详细讲解哈希算法,那需要更多的文章篇幅,我在这里就不介绍了。初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址(PS:这是一种算法,数据结构里面有提到。在某一个地址上(对应一个哈希值,该值并不特指内存地址),存储的是一个链表。在put一个新值时,根据该新值计算出哈希值,找到相应的位置,发现该位置已经蹲了一个,则新值就链接到旧值的下面,由旧值指向(next)它(也可能是倒过来指。。。)。可以参考HashMap)。
    这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
    所以,Java对于eqauls方法和hashCode方法是这样规定的:
1、如果两个对象相同,那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同
    上面说的对象相同指的是用eqauls方法比较。
    你当然可以不按要求去做了,但你会发现,相同的对象可以出现在Set集合中。同时,增加新元素的效率会大大下降。

怎么重写HashCode?

下面介绍如何来重写hashCode()方法。通常重写hashCode()方法按以下设计原则实现。

(1)把某个非零素数,例如17,保存在int型变量result中。

(2)对于对象中每一个关键域f(指equals方法中考虑的每一个域)参照以下原则处理。

boolean型,计算(f?0:1)。

byte、char和short型,计算(int)f。

long型,计算(int)(f^(f>>32))。

float型,计算Float.floatToIntBits(f)。

double型,计算Double.doubleToLongBits(f)得到一个long,再执行long型的处理。

对象引用,递归调用它的hashCode()方法。

数组域,对其中的每个元素调用它的hashCode()方法。

(3)将上面计算得到的散列码保存到int型变量c,然后执行result = 37 * result + c。

(4)返回result。



类 HashMap<K,V>

java.lang.Object
  java.util.AbstractMap<K,V>     
     java.util.HashMap<K,V>

  • 类型参数:

  • K - 此映射所维护的键的类型

  • V - 所映射值的类型

  • 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

HashCode和HashMap之间的关系

先如下代码:

import java.util.HashMap;  
public class Test {  
  
    //重写Equals不重写HashCode  
    static class Key {  
        private Integer id;  
        private String value;  
          
        public Key(Integer id, String value) {  
            super();  
            this.id = id;  
            this.value = value;  
        }  
        @Override  
        public boolean equals(Object o) {  
            if(o == null || !(o instanceof Key)) {  
                return false;  
            }else {  
                return this.id.equals(((Key)o).id);  
            }  
        }  
    }  
    //重写Equals也重写HashCode  
        static class Key_ {  
            private Integer id;  
            private String value;  
              
            public Key_(Integer id, String value) {  
                super();  
                this.id = id;  
                this.value = value;  
            }  
            @Override  
            public boolean equals(Object o) {  
                if(o == null || !(o instanceof Key_)) {  
                    return false;  
                }else {  
                    return this.id.equals(((Key_)o).id);  
                }  
            }  
            @Override  
            public int hashCode() {  
                 return id.hashCode();  
            }  
               
        }  
    public static void main(String[] args) {  
        //test hashcode  
        HashMap<Object, String> values = new HashMap<Object, String>(5);  
        Test.Key key1 =   new Test.Key(1, "one");  
        Test.Key key2 =   new Test.Key(1, "one");  
        System.out.println(key1.equals(key2));  
        values.put(key1, "value 1");  
        System.out.println(values.get(key2));  
          
        Test.Key_ key_1 =   new Test.Key_(1, "one");  
        Test.Key_ key_2 =   new Test.Key_(1, "one");  
        System.out.println(key_1.equals(key_2));  
        System.out.println(key_1 == key_2);  
        values.put(key_1, "value 1");  
        System.out.println(values.get(key_2));  
    }  
}

输出如下:由上述例子可见:只重写了equasl方法的Key类 在用做Hash中的键值的时候 两个equasl为true的对象不能获取相应 的Value的而重写了hashCode方法和equals方法的key_类 两个相等的对象 可以获取同一个Value的,这样更符合生活中 的逻辑HashMap对象是根据Key的hashCode来获取对应的Vlaue 因而两个HashCode相同的对象可以获取同一个Value

<span style="color:#cc66cc;">  
</span>


© 著作权归作者所有

共有 人打赏支持
ForingY
粉丝 23
博文 272
码字总数 156129
作品 0
杭州
程序员
HashMap的工作原理及HashMap和Hashtable的区别

HashMap的工作原理是近年来常见的Java面试题。几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别,那么为何这道面试题如此特殊呢?是因为这道题考...

袁梓皓
2016/03/09
91
0
Hashmap和HashTable

1. 关于HashMap的一些说法: HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap的底层结构是一个数组,数组中的每一项是一条链表。 HashMap的实例有俩个参数影响其...

就是小灰灰
2017/07/17
0
0
Hashtable HashMap ConcurrentHashMap 源码分析

1.Hashtable与HashMap区别比较 先来说说这两者之间的不同: 1.Hashtable 是JDK1.2出现的, 父类继承Dictionary 实现的是Map, HashMap父类是AbstractMap实现的Map public class Hashtable exte...

陈小扁
2016/03/10
230
0
Java集合浅析

Java集合类框架图 Collection List | 类型 | 数据结构 | 查询速度 | 插入速度 | 是否线程安全 || ArrayList | 数组 | 快 | 慢 | 否 || LinkList | 双向链表,| 慢 | 快 | 否 | Vector 数组 ...

wjk_snail
2016/01/19
86
0
再谈HashMap与HashTable,引入TreeMap浅谈

(1)首先说明HashMap与HashTable: HashMap是线程不安全的,是对HashTable的轻量级实现,都是对双列数据的存储。HashMap是在jdk1.2引进的对Map的实现,HashTable出现较早。 HashMap允许null-...

hanzhankang
2014/01/17
0
0
集合——HashMap的工作原理

http://www.importnew.com/16301.html 好的链接 HashMap的工作原理? 1.HashMap的底层结构是数组加链表; a.HashMap包含一个Entry(key,value,next,hash)的内部类,key/value放入HashMap的时候...

亚特兰缇斯
2015/03/03
0
0
Hashtable和HashSet的区别

作者博客@Stone原地址 HashMap和Hashtable都实现了Map接口 但决定用哪一个之前先要弄清楚它们之间的分别 主要的是线程安全性,同步(synchronization),以及速度 区别 HashMap是非synchronize...

1314Stone
2017/11/24
0
0
常见面试题——HashMap工作原理的介绍!

HashMap的工作原理是近年来常见的Java面试题。几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间的区别,那么为何这道面试题如此特殊呢?是因为这道题考...

欧阳海阳
06/24
0
0
HashMap 工作原理

HashMap的工作原理是近年来常见的Java面试题。几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别,那么为何这道面试题如此特殊呢?是因为这道题考...

The_flying_pig
2017/09/19
0
0
HashMap vs. TreeMap vs. Hashtable vs.LinkedHashMap

Map概览 Java SE中有四种常见的Map实现——HashMap, TreeMap, Hashtable和LinkedHashMap。如果我们使用一句话来分别概括它们的特点,就是: HashMap就是一张hash表,键和值都没有排序。 Tree...

markGao
2014/01/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Kafka设计解析(一)- Kafka背景及架构介绍

原创文章,转载请务必将下面这段话置于文章开头处。(已授权InfoQ中文站发布) 本文转发自技术世界,原文链接 http://www.jasongj.com/2015/03/10/KafkaColumn1 摘要   Kafka是由LinkedI...

mskk
9分钟前
0
0
使用Service Mesh整合您的微服务架构

在微服务架构的世界中,它正在达到这样的程度,即管理系统的复杂性对于利用它带来的好处变得至关重要。 目前,如何实现这些微服务不再是一个问题,因为有很多可用的框架(Spring Boot,Vert....

xiaomin0322
12分钟前
0
0
看看 LinkedList Java 9

终于迎来了 LinkedList 类,实现的接口就有点多了 Serializable, Cloneable, Iterable<E>, Collection<E>, Deque<E>, List<E>, Queue<E>。LinkedList是一个实现了List接口和Deque接口的双端链......

woshixin
30分钟前
0
0
算法 - 冒泡排序 C++

大家好,我是ChungZH。今天我给大家讲一下最基础的排序算法:冒泡排序(BubbleSort)。 冒泡排序算法的原理如下: 比较相邻的元素。如果第一个比第二个大(可以相反),就交换他们两个。 对每...

ChungZH
33分钟前
0
0
jquery ajax request payload和fromData请求方式

请求头的不同 fromData var data = { name : 'yiifaa'};// 提交数据$.ajax('app/', { method:'POST', // 将数据编码为表单模式 contentType:'application/x-ww...

lsy999
35分钟前
0
0
阿里P7架构师,带你点亮程序员蜕变之路

前言: Java是现阶段中国互联网公司中,覆盖度最广的研发语言。 掌握了Java技术体系,不管在成熟的大公司,快速发展的公司,还是创业阶段的公司,都能有立足之地。 有不少朋友问,成为Java架...

Java大蜗牛
37分钟前
1
0
Ecstore 在没有后台管理界面(维护)的情况如何更新表的字段

window 系统: 切换到:app\base 目录下: C:\Users\qimh>d: D:\>cd D:\WWW\huaqh\app\base 执行:D:\WWW\huaqh\app\base>cmd update linux 系统: 1># cd /alidata/www.novoeshop.com/app/......

qimh
41分钟前
0
0
设计模式-策略模式

策略模式 解释 对工厂模式的再次封装,使用参数控制上下文信息(将工厂返回的实例赋值给context field) 不会返回bean实例,只是设置对应的条件 调用context的方法(调用field的方法) 用户只...

郭里奥
44分钟前
0
0
python使用有序字典

python自带的collections包中有很多有用的数据结构可供使用,其中有个叫OrderedDict类,它可以在使用的时候记录元素插入顺序,在遍历使用的时候就可以按照原顺序遍历。 a = {"a":1,"b"...

芝麻糖人
今天
0
0
RestTemplate HttpMessageConverter

RestTemplate 微信接口 text/plain HttpMessageConverter

微小宝
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部