Hash算法分析
学了这么久的java,但一直不知道HashCode到底是怎么回事,现在对HashCode做一下简单的分析。
java中集合分为两类,一类是List,一类是Set。前者是有序的,可以存储重复的元素,后者是无序的,不可存放重复的元素。那现在
就有一个问题,Set是怎么判断一个元素是否重复的。Object类中有个equals()方法,用它来判断一个元素是否重复,但如果每添加一
个元素就进行一次判断,那当元素过多的时候,很明显会降低效率,于是有了哈希表的原理。HashCode的方法实际上是返回了对象的地址
当存放一个元素时会调用Hashcode方法,如果返回的地址没有存放对象时就直接把要存放的对象存入,如果相同,则进行equals()来比
较两个对象是否相同,如果相同则不再存入,否则进行散列,存放到其他位置。所以比较的次数减小了,(1--2次)效率自然就高了。
这里我们首先要明白一个问题:
equals()相等的两个对象,hashcode()一定相等;
equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有
可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。 反过来:hashcode()不等,一定能推出equals()也不等;
hashcode()相等,equals()可能相等,也可能不等。
//测试代码
public class Test1 {
public static void main(String []arg){
String s1 = new String("aaa");
String s2 = new String("aaa");
String s3 = "aaa";
String s4 = "aaa";
System.out.println(s1 == s2); //false
System.out.println(s3 == s4); //true
System.out.println(s1 == s3); //false
System.out.println(s1.hashCode()); //96321
System.out.println(s2.hashCode()); //96321
System.out.println(s3.hashCode()); //96321
System.out.println(s4.hashCode()); //96321
}
}
上述代码说明两个对象不相等,但hashCode可能相同。
下面我们一起来分析一下hashcode在HashSet,HashMap和Hashtable中的使用。
HashSet是实现了Set接口,存放的元素时无序性的,不可重复的,那它是怎么判断一个元素是否重复的呢,我们来分析一下。
java中判断两个元素相等的规则有两点:
1),判断两个对象的hashCode是否相等
如果不相等,认为两个对象也不相等,完毕
如果相等,转入2)
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必
需的。)
2),判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条
准则进行限制,才能保证加入的为非重复元素。
//测试代码
/**
*学生类
*/
public class Student {
String name;
public Student(String name){
this.name = name;
}
}
//***************************************************
import java.util.HashSet;
import java.util.Iterator;
public class Test2 {
public static void main(String []arg){
Student s1 = new Student("zhao");
Student s2 = new Student("qian");
Student s3 = new Student("sun");
Student s4 = new Student("zhao");
HashSet<Student> set = new HashSet<Student>();
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
System.out.println(s1.hashCode());
System.out.println(s4.hashCode());
Iterator<Student> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next().name);
}
}
}
输出结果:
427340025
594763966
zhao
zhao
qian
sun
这个结果说明了set中存放了两个相同的元素。那是不是它违背了Set存放元素的原则呢?其实并不是的。那为什么用String创建的两个对象
的hashcode值是相同的呢。
String中的hashcode方法:
public int hashCode() {
int h = hash;
if (h == 0 && count > 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
它是重写了父类的hashcode方法,而我们定义的Student类并没有重写Object类的hashcode方法,因此他会存入两个相同的元素。
对Student类进行修改,代码如下:
public class Student {
String name;
public Student(String name){
this.name = name;
}
//重写父类的hashCode方法
public int hashCode(){
return this.name.hashCode();
}
//重写equals方法
public boolean equals(Object o){
Student s = (Student)o;
return s.name.equals(this.name);
}
}
再次运行
结果:
3737564
3737564
zhao
qian
sun
这样它就不会再加入相同的元素了。
这里要进一步说明一下,判断两个对象是否相同,用equals方法完全可以判断,加hashcode方法并不是必须的,而加hashcode方法主要
是为了提高效率,因为如果每次都进行equals方法进行判断的话会降低效率。(为什么就不用再解释了)
明白了Hash的原理,我们就可以自己写一个Hash的程序了。最基本的功能(增,删,改,查),当处理比较多的数据时,我们看看自己
写的Hash集合是否能应付得了这些数据。
我们自己写一个Hash集合的时候要注意,如果我们把每个数据看成一个节点,那我们首先要考虑的是用什么数据结构来存放这些节点,如
果用数组虽然会方便查找,但对处理大数据的时候则要考虑内存是否够用,而且对数据的增删操作会比较复杂。而用链表有不方便查找,
所以我们要用两者的结合体,创建一个数组,每个单元存放一个链表。首先我们通过自己写的hashcode方法确定该节点要放的位置,如果
没有冲突,则放入。否则放到该节点所在的链表上。
//确定节点存放位置的方法(求余数)
public int getIndex(HashCode code, int size) {
return code.getKey() % size;
}
另一点我们要考虑的是我们不能无限的存放数据,首先我们创建的是数组,它有固定长度,数据存放的越多,造成的冲突就越多,因此我们
要考虑当数据超过一个标准时要重新创建一个数组,然后把原来所有的节点重新排列到新的数组中。我们把这个标准叫做阀值。
下面是具体操作的代码:
/**
* 加hash节点
*/
public void addCode(HashCode newcode) {
if (length / size == load) { // 如果超过阀值
System.out.println("");
System.out.println("reflash");
reflash(); // 重新分配节点
}
int index = getIndex(newcode, size); // 得到索引
if (HashArray[index] == null) { // 当前位置还没放入元素
HashArray[index] = newcode; // 将节点加入数组
HashCode lastcode = null;
newcode.setNext(lastcode);
} else {
HashCode code = HashArray[index];
newcode.setNext(code.getNext());
code.setNext(newcode);
}
length++;
}
/**
* 删除节点
* @param key 节点的key
* @param data 节点的data
*/
public void deleteCode(int key, int data) {
int lay = key % size;
if (HashArray[lay].getKey() == key && HashArray[lay].getData() == data) {
HashArray[lay] = HashArray[lay].getNext();
} else {
HashCode code = HashArray[lay];
while (code.getNext().getData() != data
&& code.getNext().getKey() != key) {
code = code.getNext();
}
code.setNext(code.getNext().getNext());
}
}
/**
* 重新分配数组中所有节点
*/
public void reflash() {
HashCode newHashArray[] = new HashCode[size + 5]; // 创建一个新数组
for (int i = 0; i < size; i++) { // 遍历所有节点
HashCode code = HashArray[i];
while (code != null) {
HashCode newcode = code;
index = getIndex(newcode, (size + 5));
if (newHashArray[index] == null) {
newHashArray[index] = newcode;
HashCode lastcode = null;
newcode.setNext(lastcode);
} else {
newcode.setNext(newHashArray[index].getNext());
newHashArray[index].setNext(newcode);
}
code = code.getNext();
}
}
HashArray = newHashArray;
size += 5;
System.out.println("扩充后size:" + size);
}
简单的hash集合写完之后并不代表结束,我们可以进行一下测试,然后与系统的Hash进行比较。下面说一下我自己的比较结果。
只是加入节点的操作(10000个节点),我的运行时间是500ms,而系统的只要20ms,这就说明我们还有很多要改进的地方,当然看系统
的源代码是最好的方法,但由于我一直没耐心把系统的程序看完,所以现在还不知道该怎么改。
分享到:
相关推荐
MurmurHash算法由Austin Appleby创建于2008年,现已应用到Hadoop、libstdc 、nginx、libmemcached,Redis,Memcached,Cassandra,HBase,Lucene等开源系统。2011年Appleby被Google雇佣,随后Google推出其变种的...
在IT领域,Hash算法是一种广泛应用于数据验证、存储和比较的技术。它将任意长度的数据转换成固定长度的输出,通常称为Hash值或指纹。在这个压缩包中,我们重点关注的是图像的相似度Hash算法,特别是平均哈希算法(a...
Java实现GeoHash算法是一种在IT领域中用于地理位置数据存储和检索的技术。GeoHash将经纬度坐标转换为字符串,使得地理位置可以被高效地索引和查询。这种算法利用了空间分割和编码策略,使得相邻的位置在编码后具有...
Hash函数集合,包含主流的hash函数: nginx_hash算法,OpenSSL_hash算法,RSHash,JSHash,PJWHash,ELFHash,BKDRHash,DJBHash,DEKHash,APHash等等!
GeoHash算法是一种基于地理坐标的分布式空间索引技术,它通过将地球表面的经纬度坐标转化为可比较的字符串,使得我们可以高效地进行地理位置的搜索、范围查询以及邻居查找等操作。这种算法尤其适用于大数据和分布式...
"Hash算法MD5实验报告材料" 本实验报告主要介绍了Hash算法MD5的实验报告,旨在通过实际编程来了解MD5算法的加密和解密过程,并加深对Hash算法的认识。 一、Hash算法的定义 Hash算法是一种将输入数据转换为固定...
### 常见的HASH算法解析 在计算机科学领域,哈希算法(HASH算法)是一种将任意长度的数据映射到固定长度数据的算法,通常用于数据查找、密码存储、消息完整性验证等多种场景。本文将详细介绍几种常见的哈希算法,...
### 经典Hash算法概述与实现 #### 一、引言 哈希算法在计算机科学领域扮演着极其重要的角色,特别是在数据检索、信息安全以及数据完整性校验等方面。它能够将任意长度的数据转换成一个固定长度的哈希值,这一过程在...
一个hash算法的工具类,里面包含了一些常用的hash算法
### Hash算法相关介绍 在计算机科学领域,哈希(Hash)是一种将任意长度的数据映射为固定长度数据的技术。哈希算法广泛应用于多种场景中,包括但不限于数据完整性验证、密码存储、快速查找等。本文主要介绍了几种...
### 一致性 Hash 算法详解 #### 一、引言 一致性 Hash 算法是一种特殊的哈希算法,主要用于解决分布式系统中节点增删时数据重定位的问题。该算法最早于1997年在《Consistent hashing and random trees》这篇论文中...
在IT领域,哈希算法(Hash Algorithm)是一种用于将任意长度的数据转化为固定长度输出的算法。这个过程通常称为哈希或散列。哈希算法在信息安全、数据完整性验证、密码学等多个方面都有着广泛的应用。本项目是用...
在计算机科学中,哈希(Hash)算法是一种用于将任意长度的数据映射为固定长度输出的函数。这种输出通常称为哈希值或消息摘要。在Java编程语言中,实现哈希算法可以方便地用于数据验证、查找表以及密码存储等多种用途...
### 安全Hash算法SHA-1的实现 #### 一、Hash函数与数据完整性 Hash函数在现代密码学中扮演着至关重要的角色,它能够确保数据的完整性和一致性。一个典型的Hash函数接受任意长度的数据输入,并产生固定长度的输出,...
### Hash算法介绍 #### 一、Hash算法概述 Hash算法是一种在计算机科学中广泛应用的数据处理技术,主要用于将输入数据(通常称为“键”)转换为固定长度的输出(通常称为“哈希值”或“散列值”)。这种转换过程是...
Geohash算法实现,经纬度到geohash编码的实现
网上有很多geohash算法的实现,都是基于java或者php代码实现的,没有sql实现的版本,这里使用mysql简单实现了这个算法
Hash算法在IT行业中扮演着至关重要的角色,尤其是在信息安全和数据完整性验证方面。本实验主题为“Hash算法实验”,主要涉及的是密码学中的消息摘要技术,具体是使用MD5(Message-Digest Algorithm 5)算法对文件...
Geohash算法就是将经纬度编码,将二维变一维,给地址位置分区的一种算法 此檔案為C語言實現 函式庫使用介紹: 1)編碼 char* geohash_encode(double lat, double lng, int precision); 以所需精度獲取緯度和經度並...