HashMap/HashSet,hashCode,哈希表

 hash code、equals和“==”三者的关系java

 1) 对象相等则hashCode必定相等;
  2) hashCode相等对象未必相等。c++

== 是比较地址是否相等,JAVA中声明变量都是引用嘛,不一样的引用,可能指向同一个地址。程序员

equals 是比较值是否相等。面试

 

1.若是是基本变量,没有hashcode和equals方法,基本变量的比较方式就只有==,;

2.若是是变量,因为在java中全部变量定义都是一个指向实际存储的一个句柄(你能够理解为c++中的指针),在这里==是比较句柄的地址(你能够理解为指针的存储地址),而不是句柄指向的实际内存中的内容,若是要比较实际内存中的内容,那就要用equals方法,可是!!!

若是是你本身定义的一个类,比较自定义类用equals和==是同样的,都是比较句柄地址,由于自定义的类是继承于object,而object中的equals就是用==来实现的,你能够看源码。

那为何咱们用的String等等类型equals是比较实际内容呢,是由于String等经常使用类已经重写了object中的equals方法,让equals来比较实际内容,你也能够看源码。

3. hashcode
在通常的应用中你不须要了解hashcode的用法,但当你用到hashmap,hashset等集合类时要注意下hashcode。

你想经过一个object的key来拿hashmap的value,hashmap的工做方法是,经过你传入的object的hashcode在内存中找地址,当找到这个地址后再经过equals方法来比较这个地址中的内容是否和你原来放进去的同样,同样就取出value。

因此这里要匹配2部分,hashcode和equals
但假如说你new一个object做为key去拿value是永远得不到结果的,由于每次new一个object,这个object的hashcode是永远不一样的,因此咱们要重写hashcode,你能够令你的hashcode是object中的一个恒量,这样永远能够经过你的object的hashcode来找到key的地址,而后你要重写你的equals方法,使内存中的内容也相等。。。算法

 

 

首先,从语法角度,也就是从强制性的角度来讲,hashCode和equals是两个独立的,互不隶属,互不依赖的方法,equals成立与hashCode相等这两个命题之间,谁也不是谁的充分条件或者必要条件。  
   
  可是,从为了让咱们的程序正常运行的角度,咱们应当向Effective   Java中所言  
   
  重载equals的时候,必定要(正确)重载hashCode  
   
  使得equals成立的时候,hashCode相等,也就是a.equals(b)->a.hashCode()   ==   b.hashCode(),或者说此时,equals是hashCode相等的充分条件,hashCode相等是equals的必要条件(从数学课上咱们知道它的逆否命题:hashCode不相等也不会equals),可是它的逆命题,hashCode相等必定equals以及否命题不equals时hashCode不等都不成立。  
   
  因此,若是面试的时候,最好把hashCode与equals之间没有强制关系,以及根据(没有语法约束力的)规范的角度,应当作到...这两层意思都说出来:P  编程

  总结一下,equals()是对象相等性比较,hashCode()是计算对象的散列值,固然他们的依据是对象的属性。数组

 对于equals,通常咱们认为两个对象同类型而且全部属性相等的时候才是相等的,在类中必须改写equals,由于Object类中的equals只是判断两个引用变量是否引用同一对象,若是不是引用同一对象,即便两个对象的内容彻底相同,也会返回false。固然,在类中改写这个equals时,你也能够只对部分属性进行比较,只要这些属性相同就认为对象是相等的。  
   
  对于hashCode,只要是用在和哈希运算有关的地方,前面不少兄弟都提到了,和equals同样,在你的类中也应该改写。固然若是两个对象是彻底相同的,那么他们的hashCode固然也是同样的,可是象前面所述,规则能够由你本身来定义,所以二者之间并无什么必然的联系。  
   
  固然,大多数状况下咱们仍是根据全部的属性来计算hashCode和进行相等性比较。安全

 

 

  HashMap实现了Map接口,该接口的做用主要是为客户提供三种方式的数据显示:只查看keys列表;只查看values列表,或以key-value形式成对查看。Map接口并无定义数据要如何存储,也没有指定如何断定key是同样,所以并非全部的Map实现都会与hashCode方法扯上关系,如TreeMap即是要求对象实现Comparator接口,经过其compare方法来比对二者是否一致,而非hashCode及equals。同理,若是咱们本身实现Map接口,咱们也能够直接使用数组进行数据存储使用==断定key值是否一致,依然能够彻底知足Map接口的定义。数据结构

就是一个键值对应的集合,包含put,get,equals,hashCode
HashMap a = new HashMap();
  a.put("name", "abcdef"); // key是name,value是字符串abcdef
HashMap是基于HashCode的

Hash容器的基本要求应该有以下几点:多线程

知足Hash表的查找要求(废话)
能支持从小数据量到大数据量的自动转变(自动扩容)
使用挂链法解决冲突

 在Hash表中,记录在表中的位置和其关键字之间存在着一种肯定的关系。这样 咱们就能预先知道所查关键字在表中的位置,从而直接经过下标找到记录。

 

HashSet无序,不可重复,线程非同步。底层是哈希表结构

 

散列表(Hash table,也叫哈希表),是根据关键字(Key value)而直接进行访问的数据结构。
也就是说,它经过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫作散列函数,存放记录的数组叫作散列表。

HashMap底层就是散列表数据结构,即数组和链表的结合体,
底层是一个数组结构,数组中的每一项又是一个链表。这样作有什么好处呢?
数组可以提供对元素的快速访问但不易于扩展(若是不知道元素脚标,还得进行遍历查找),链表易于扩展但不能对其元素进行快速访问。
怎样作到一箭双鵰,就是散列表数据结构

 

元素(key,value)在HashMap中被封装进Entry数组。
put元素的时候,根据key的hash值定位元素在Entry数组中的索引,若是当前索引有元素,就经过equals方法进行比较,将元素存进当前Entry的链表中适当的位置。
get元素的时候,根据key的hash值定位元素在Entry数组中的索引,而后经过equals方法定位元素在链表中的位置,取出该元素。

性能分析:

由于元素的存取是经过hash算法进行的,因此速度都没的说。
在查找操做中,惟一影响性能的是在链表中,但实际只要优化好了key对象hashCode跟equals方法,就会避免链表中的数据过多而致使查找性能变慢。

 

  1. hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优势,例如,java.util.Hashtable 提供的哈希表。   
  2.   
  3. hashCode 的常规协定是:   
  4. 在 Java 应用程序执行期间,在同一对象上屡次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另外一次执行,该整数无需保持一致。   
  5. 若是根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每一个对象上调用 hashCode 方法都必须生成相同的整数结果。   
  6. 如下状况不 是必需的:若是根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法一定会生成不一样的整数结果。可是,程序员应该知道,为不相等的对象生成不一样整数结果能够提升哈希表的性能。   
  7. 实际上,由 Object 类定义的 hashCode 方法确实会针对不一样的对象返回不一样的整数。(这通常是经过将该对象的内部地址转换成一个整数来实现的,可是 JavaTM 编程语言不须要这种实现技巧。)   
  8.   
  9. 当equals方法被重写时,一般有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具备相等的哈希码。  

 

  1. 1.hashcode是用来查找的,若是你学过数据结构就应该知道,在查找和排序这一章有  
  2. 例如内存中有这样的位置  
  3. 0  1  2  3  4  5  6  7    
  4. 而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,若是不用hashcode而任意存放,那么当查找时就须要到这八个位置里挨个去找,或者用二分法一类的算法。  
  5. 但若是用hashcode那就会使效率提升不少。  
  6. 咱们这个类中有个字段叫ID,那么咱们就定义咱们的hashcode为ID%8,而后把咱们的类存放在取得得余数那个位置。好比咱们的ID为9,9除8的余数为1,那么咱们就把该类存在1这个位置,若是ID是13,求得的余数是5,那么咱们就把该类放在5这个位置。这样,之后在查找该类时就能够经过ID除 8求余数直接找到存放的位置了。  
  7.   
  8. 2.可是若是两个类有相同的hashcode怎么办那(咱们假设上面的类的ID不是惟一的),例如9除以8和17除以8的余数都是1,那么这是否是合法的,回答是:能够这样。那么如何判断呢?在这个时候就须要定义 equals了。  
  9. 也就是说,咱们先经过 hashcode来判断两个类是否存放某个桶里,但这个桶里可能有不少类,那么咱们就须要再经过 equals 来在这个桶里找到咱们要的类。  
  10. 那么。重写了equals(),为何还要重写hashCode()呢?  
  11. 想一想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不经过重写hashcode()来找到桶,光重写equals()有什么用啊  

 

哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。若是详细讲解哈希算法,那须要更多的文章篇幅,我在这里就不介绍了。 
初学者能够这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并非)。   
这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一会儿能定位到它应该放置的物理位置上。 
若是这个位置上没有元素,它就能够直接存储在这个位置上,不用再进行任何比较了;若是这个位置上已经有元素了, 
就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。 
因此这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大下降了,几乎只须要一两次。   
因此,Java对于eqauls方法和hashCode方法是这样规定的: 
一、若是两个对象相同,那么它们的hashCode值必定要相同;二、若是两个对象的hashCode相同,它们并不必定相同     上面说的对象相同指的是用eqauls方法比较。   
你固然能够不按要求去作了,但你会发现,相同的对象能够出如今Set集合中。同时,增长新元素的效率会大大降低。

 

Java中的集合(Collection)有两类,一类是List,再有一类是Set。 
你知道它们的区别吗?前者集合内的元素是有序的,元素能够重复;后者元素无序,但元素不可重复。 
那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢? 
这就是Object.equals方法了。可是,若是每增长一个元素就检查一次,那么当元素不少时,后添加到集合中的元素比较的次数就很是多了。 

 

hashcode这个方法是用来鉴定2个对象是否相等的。
那你会说,不是还有equals这个方法吗?

不错,这2个方法都是用来判断2个对象是否相等的。可是他们是有区别的。

通常来说,equals这个方法是给用户调用的,若是你想判断2个对象是否相等,你能够重写equals方法,而后在代码中调用,就能够判断他们是否相等了。简单来说,equals方法主要是用来判断从表面上看或者从内容上看,2个对象是否是相等。举个例子,有个学生类,属性只有姓名和性别,那么咱们能够认为只要姓名和性别相等,那么就说这2个对象是相等的。

hashcode方法通常用户不会去调用,好比在hashmap中,因为key是不能够重复的,他在判断key是否是重复的时候就判断了hashcode这个方法,并且也用到了equals方法。这里不能够重复是说equals和hashcode只要有一个不等就能够了!因此简单来说,hashcode至关因而一个对象的编码,就好像文件中的md5,他和equals不一样就在于他返回的是int型的,比较起来不直观。咱们通常在覆盖equals的同时也要覆盖hashcode,让他们的逻辑一致。举个例子,仍是刚刚的例子,若是姓名和性别相等就算2个对象相等的话,那么hashcode的方法也要返回姓名的hashcode值加上性别的hashcode值,这样从逻辑上,他们就一致了。

要从物理上判断2个对象是否相等,用==就能够了。



1.hashMap去掉了HashTable 的contains方法,可是加上了containsValue()和containsKey()方法。

2.hashTable同步的,线程安全,而HashMap是非同步的,线程不安全,效率上逼hashTable要高。

3.hashMap容许空键值,而hashTable不容许。

4.HashTable中hash数组默认大小是11,增长的方式是 old*2+1。HashMap中hash数组的默认大小是16,并且必定是2的指数。

5.Hashtable是Dictionary的子类,HashMap是Map接口的一个实现类;

 

HashMap实现同步的方法:调用Collections的静态方法Collections.synchronizedMap(Map map);,返回一个同步的Map,这个Map封装了底层的HashMap的全部方法,使得底层的HashMap即便是在多线程的环境中也是安全的。

 

同时说一下,ArrayList和HashSet,他们也都不是同步的,都是线程不安全的,对其实现同步的方式和HashMap的方式相同,都容许使用null元素,ArrayList分配的初始空间为10,HashSet分配的初始空间为16

相关文章
相关标签/搜索