什么是hash算法
Hash:
- 一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。
- 这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
- hash就是找到一种数据内容和数据存放地址之间的映射关系。
-
对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称碰撞。
-
根据散列函数H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的"象" 作为记录在表中的存储位置,这种表便称为散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。
hash表
根据散列函数H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的"象" 作为记录在表中的存储位置,这种表便称为散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。
hash函数
·直接取余法:f(x):= x mod maxM ; maxM一般是不太接近 2^t 的一个质数。
·乘法取整法:f(x):=trunc((x/maxX)*maxlongit) mod maxM,主要用于实数。
·平方取中法:f(x):=(x*x div 1000 ) mod 1000000); 平方后取中间的,每位包含信息比较多。
散列函数的性质:
所有散列函数都有如下一个基本特性:
1.如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。
2.散列函数的输入和输出不是一一对应的,如果两个散列值相同,两个输入值很可能是相同的,但并不能绝对肯定二者一定相等。输入一些数据计算出散列值,然后部分改变输入值,一个具有强混淆特性的散列函数会产生一个完全不同的散列值。
3.典型的散列函数都有无限定义域,比如任意长度的字节字符串,和有限的值域,比如固定长度的比特串。在某些情况下,散列函数可以设计成具有相同大小的定义域和值域间的一一对应。一一对应的散列函数也称为排列。可逆性可以通过使用一系列的对于输入值的可逆"混合"运算而得到。
hash函数的构造准则:简单、均匀
1、 散列函数的计算简单,快速;
2、 散列函数能将关键字集合K均匀地分布在地址集{0,1,…,m-1}上,使冲突最小。
hash函数的构造方法:
1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)
2. 数字分析法
3. 平方取中法
4. 折叠法
5. 随机数法
6. 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p,p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。
捕获对象
思考:
java虚拟机是如何去内存里面获取到我们想要的对象呢?
问题:
站在JAVA虚拟机的角度,在内存里面有好多好多个对象,这里用椭圆来代表一个个对象。那么对于JAVA虚拟机来说,它运行的时候需要找到这些对象的地址,这些对象的地址怎么找呢?
解析:
JAVA虚拟机会用一张表记录每一个对象在什么位置上,而这张表一般是用哈希编码来记录,每一个对象都有自己独一无二的哈希编码,根据这个编码就可以找到相关的对象,也就是说,根据这个编码你可以独一无二地确定这个对象,并且可以非常快地确定这个对象所在的位置,可以简单这么理解哈希编码的作用。
hashcode方法
在object类中,hashcode()方法是本地方法,返回的是对象的地址值。
equals方法
在object类中有一个方法叫equals(),用于判读两个对象是否相等。
当我们写了一个自己的class,然后用class new了两个对象,如果在这两个对象上用equals时,此时比较的两个引用是不是一样,也就是他们的物理地址是不是一样,如果不一样的话,就会返回false.
我们实际用的时候,往往不是希望比较两个对象的物理地址是不是一样,而比较两个对象的属性等东西是不是一样,所以Object提供的方法往往不能满足我们要求。
这就需要我们覆盖Object的equals方法。
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象hashCode()方法来计算hashcode,然后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
HashSet排除重复
Java中的set默认情况下,会根据是否同一个对象来判断是否重复。
HashSet在判断两个元素是否重复的时候,用调用hashcode方法和equals方法。
只有在两个对象的hashcode方法返同一个值,并且equals为true的情况下,它才会认为两个是重复对象。先调用hashcode,如果只一样,再调用equals。
下面以set的一个实现类HashSet为例,简单介绍一下set不重复实现的原理:
1 2 3 4 5 6 7 8 9 10 | package com.lovo.test; public class CustomString { private String value; public CustomString() { this(""); } public CustomString(String value) { this.value = value; } } |
1 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 | public class HashSetTest { public static void main(String[] args) { String a = new String("A"); String b = new String("A"); CustomString c = new CustomString("B"); CustomString d = new CustomString("B"); System.out.println("a.equals(b) == " + a.equals(b)); System.out.println("c.equals(d) == " + c.equals(d)); Set<Object> set = new HashSet<Object>(); set.add(a); set.add(b); set.add(c); set.add(d); System.out.println("set.size() == " + set.size()); for (Object object : set) { System.out.println(object); } } } /* 运行结果如下: a.equals(b) == true c.equals(d) == false set.size() == 3 com.darren.test.overide.CustomString@2c39d2 A com.darren.test.overide.CustomString@5795ce */ |