`
981875739
  • 浏览: 7912 次
  • 性别: Icon_minigender_2
  • 来自: 长沙
社区版块
存档分类
最新评论

MyHash实现

 
阅读更多

经过一段时间的研究学习,对于Hash有了更深一步的了解和认识,下面谈谈一些基本概念及自己设计的一个MyHash的实现。


1、基本概念:
Hash,一般翻译做“散列”,也有直接音译为“哈希”。
哈希就是把任意长度的输入通过散列算法,变换成固定长度的输出,该输出就是散列值。

Hash主要用于信息安全领域中加密算法,把一些不同长度的信息转化成杂乱的128位编,这些编码值叫哈希值也可以说哈希就是找到一种数据内容和数据存放地址之间的映射关系。
哈希表就是一种根据关键码值直接进行访问的数据结构。
 
2、哈希表的优缺点:
那么为什么使用哈希表呢?它一定有特定的优点我们才会使用它。看看这个图表,相信大家就了解了吧~~


数组具有查找迅速但是对其进行插入删除操作较困难的特点,而链表容易插入删除但查找困难。。。是否有一种数据结构能够将两者的优点集合呢?哈希表就是一个两全其美的选择~~他是直接根据key值找到相应的存储位置,对其进行访问,插入删除操作类似于链表的相关操作。哈希表是基于快速存取的角度设计的,它的优点非常明显,存取速度能达到常量级O(1)
当然它也有一定的缺点:
1、使元素丧失了有序性
2、元素不能够紧密的排列,需要足够大的空间
3、两个或多个不同项被散列到同一位置是不可避免的 ——冲突
  【冲突:对不同的关键字可能得到同一散列地址 key1key2,而f(key1)=f(key2)

3、常用的构造构造哈希函数的方法:
v1.直接寻址法
v2.数字分析法
v3.平方取中法
v4.叠加法
v5.随机数法
v6.除留余数法
比较常用的是除留余数法,下面详细介绍一下此方法:
取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址 。
H(key) = key MOD p     (p<=m)  
例如: 给定一组关键字为:          12, 39, 18, 24, 33, 21 
         H(key) = key MOD 9     3    3    0    6    6   3  
这里对P要有什么限制条件呢?                                         
12, 39, 18, 24, 33, 21
若取 p=9, 则他们对应的哈希函数值将为: 3, 3, 0, 6, 6, 3 
若取 P=18,则他们对应的哈希函数值将为12,3,0,6,15,3
若取p=24,则他们将对应的哈希函数值将为: 12,15,18,0,9,21 
一般结论:选取的P较小,冲突的可能性较大

4、解决冲突的方法:
1、拉链法:即构造成数组链表的形式
2、在哈希法:设计两种甚至多种哈希函数
3、开放地址法:线性/二次/随机探测再散列
4、建域法:建立一个公共溢出区存储发生冲突的记录

5、下面是一个我实现的MyHash:解决冲突是采用的第一种方法
package MyHash2;

import java.util.Collection;
import java.util.Iterator;

/**
 * MyHash类
 * @author Administrator
 *
 * @param <T>
 */
public class MyHash<T>implements Collection<T>{
	private Entry[] table;
	private int hashTableSize;
	private int Capacity=16;//初始大小
	private float LoadFactor= 0.75f;//默认装载因子
	private int tableThreshold;//最大承载大小
	private int modCount=0;
	
	/**
	 * 结点类
	 * @author Administrator
	 *
	 * @param <T>
	 */
	private static class Entry<T>{
		T value;//数值
		int hashvalue;//hash值
		Entry<T> next;//指针
		public Entry(T value,int hashvalue,Entry<T> next){
			this.value=value;
			this.hashvalue=hashvalue;
			this.next=next;
		}		
	}
	
	/**
	 * 构造函数
	 */
	public MyHash(){
		table=new Entry[Capacity];
		hashTableSize=0;
		tableThreshold =(int)(table.length * LoadFactor);
	}

	/**
	 * 添加数据
	 */
	public boolean add(T item){
		int hashvalue=item.hashCode();//计算hashvalue
		int index=hashvalue%table.length;//计算下标
		//System.out.println("item.hashCode()="+hashvalue+"   table.length="+table.length+"    index="+index);		
		Entry<T> entry;
		entry= table[index];
		//不为空,则查找该数据是否已存在;为空则加入
		while(entry!=null){
			if(entry.value.equals(item)){
				System.out.println("该数据已经存在");
				return false;
			}
			entry=entry.next;
		}
		modCount++;
		entry =new Entry<T>(item,hashvalue,(Entry<T>) table[index]);
		table[index]=entry;
		hashTableSize++;
		//检查hash表是否已超过最大承载量
		if(hashTableSize>tableThreshold){
			int newTableSize=2 * table.length + 1;
			rehashing(newTableSize);
		}
//		System.out.println("添加数据"+item+"成功");
		return true;
	}
	
	/**
	 * 移除数据
	 */
	public boolean remove(Object item){
		int index =item.hashCode()%table.length;
		Entry<T> current;
		Entry<T> previous=null;
		current=table[index];
		//对应下标的数组位置有数据
		while(current!=null){
			//找到要删除的数据
			if(current.value.equals(item)){
				modCount++;
				//该数据不是链表的第一个数据,则将前一个数据的指针指向下一个数据
				if(previous!=null){
					previous.next=current.next;
				}else{//该数据是链表的第一个数据,则将下一个数据挪到当前下标处
					table[index]=current.next;
				}
				hashTableSize--;
				System.out.println("删除了数据:"+current.value+"  index为"+index);
				return true;
			}else{//向下继续找要删除的数据
				previous=current;
				current=current.next;
			}
		}
		return false;
	}
	
	/**
	 * 查找某数据
	 * @param item
	 * @return
	 */
	public boolean find(Object item){
		int index=item.hashCode()%table.length;//计算出下标
		Entry<T> entry=table[index];
		//对数组下标下的链表进行查找
		while(entry!=null){			
			//与对应下标内的数据进行equals比较
			if(entry.value.equals(item)){
				System.out.println("查找到数据:"+entry.value+"  index为"+index);
				return true;
			}			
			entry=entry.next;
		}
		System.out.println("查找失败,数据不存在");
		return false;
	}
	
	/**
	 * 清除所有数据
	 */
	public void clear(){
		int len=table.length;
		//遍历———清空数据
		for(int i=0;i < len; i++){
			table[i] = null;  
		    modCount++;  
		    hashTableSize = 0;  
        }
	}

	/**
	 * rehash方法
	 * @param rehashSize
	 */
	public void rehashing(int newTableSize){
		System.out.println("Hash重构");
		Entry[] newTable =new Entry[newTableSize];//新HashTable
		Entry[] oldTable = table;//原HashTable
		int index;
		Entry entry;
		//循环,将原HashTable内的数据重新计算地址,存入新HashTable
		for(int i=0;i<table.length;i++){
			entry=table[i];
			if(entry!=null){
				//先将该位置的原数据清除,设为NUll
				table[i]=null;
				//向新HashTabele中添加该数据
				while(entry!=null){
					Entry newEntry=entry.next;
					index=entry.hashvalue%newTableSize;
					entry.next = newTable[index];  
					newTable[index] = entry;  
                    entry = newEntry;  
				}
			}
		}
		table=newTable;
		//重新设置装载量大小
		tableThreshold = (int) (table.length * LoadFactor);  
		//将原HashTable清空
		oldTable = null;  
	}
	
//	/**
//	 * 计算HashCode的方法
//	 * @return
//	 */
//	public int hashcode(Object item){
//		int index = item.hashCode()%table.length;
//		return index;
//	}
	@Override
	public int size() {
		// TODO Auto-generated method stub
		return hashTableSize;  
	}
	@Override
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return hashTableSize == 0;  
	}
	@Override
	public boolean contains(Object o) {
		// TODO Auto-generated method stub
		return false;
	}
	@Override
	public Iterator<T> iterator() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public Object[] toArray() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public <T> T[] toArray(T[] a) {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public boolean containsAll(Collection<?> c) {
		// TODO Auto-generated method stub
		return false;
	}
	@Override
	public boolean addAll(Collection<? extends T> c) {
		// TODO Auto-generated method stub
		return false;
	}
	@Override
	public boolean removeAll(Collection<?> c) {
		// TODO Auto-generated method stub
		return false;
	}
	@Override
	public boolean retainAll(Collection<?> c) {
		// TODO Auto-generated method stub
		return false;
	}
	
	
}
 
Test测试类:
加入150万个数据(打印Size、添加数据所需的时间)—>查找一个特定数据(打印该数据、数据所在数组位置的下标、查找时间)—>删除一个数据(打印Size)—>再次查找该数据(查找失败)—>清空全部数据

Hash重构十七次


添加数据时间为:1906
此时Size为1500000
查找到数据:100000  index为290089
查找数据时间为:0
删除了数据:100000  index为290089
此时Size为1499999
查找失败,数据不存在
查找到数据:100000  index为290089
清除全部数据了,此时Size为0


发现多次执行同一程序,添加数据的时间与之前相比会少。。。
  • 大小: 11.6 KB
  • 大小: 27.2 KB
分享到:
评论

相关推荐

    MyHash-master.zip

    《MyHash工具详解——深入理解哈希算法与其实现》 在信息技术领域,哈希算法是一种重要的数据处理手段,广泛应用于信息安全、数据存储、身份验证等多个方面。MyHash-master.zip是一个包含MyHash项目的压缩包,它为...

    一个c++实现的哈希表类

    ### 哈希表在C++中的实现 #### 概述 本文介绍了一个使用C++实现的哈希表类,该类使用了模板技术来增强其通用性,并且使用了除留余数法作为散列函数的基础算法。该类不仅支持基本的操作如插入、删除、查找,还提供了...

    c#哈希算法的实现方法及思路

    在给定的描述中,我们看到一个简单的哈希表类`MyHash`的实现,该类允许用户通过字符串键来存取对象。下面我们将详细讨论这个实现方法。 首先,哈希表的核心在于它能够将任意键映射到一个数组的索引,使得查找和插入...

    redis 对hash设置expires.rar

    "redis 对hash设置expires"这个主题涉及到Redis中的一个关键特性,即如何为哈希(Hash)数据类型设置过期时间(expires),这使得存储在Redis中的数据能够自动删除,从而实现内存管理。现在我们将深入探讨这个知识点...

    C#实现访问Redis数据库

    本文将详细讲解如何使用C#语言来实现对Redis数据库的访问,以便充分利用其高效特性来提升应用性能。 首先,要实现C#访问Redis,我们需要一个客户端库。StackExchange.Redis是.NET开发者广泛使用的官方推荐库,它...

    Java实现简易HashMap功能详解

    接下来,实现MyHash的基本操作,包括创建一个固定大小的数组,将数组中的每个元素作为头节点存储键值对,存储键值对时计算哈希码,将哈希码与数组做某种运算,得到该数在数组中的哪一个位置下的链表中。取数时计算该...

    程序源代码

    哈希表是一种数据结构,它使用哈希函数来存储数据,能够实现平均时间复杂度为O(1)的查找和插入操作。 首先,`hash.h`文件很可能是包含哈希类定义的头文件。在C++中,类定义通常会声明类的成员变量、构造函数、析构...

    两种方法实现Hash散列

    Hash散列是一种在计算机科学中广泛使用的数据结构和算法,...在给定的文件"MyHash"中,可能包含了这两种方法的具体实现代码,可供学习和参考。通过分析和比较这两种方法,我们可以更好地理解Hash散列的原理和优化策略。

    Vue实现根据hash高亮选项卡

    3. 动态类绑定:使用Vue的v-bind指令实现动态绑定class,当某个选项卡的href属性匹配当前的myhash变量时,相应的a标签会被赋予一个类名为“active”的样式类。 4. CSS样式:定义相应的CSS样式,通常“active”类...

    一个c++实现的哈希表类-C++文档类资源

    在程序中我们对关键字key应用散列函数H(key)来判断关键字key是否在散列表中,...程序的具体实现如下:本程序是用模板类myhash来实现,包括protected和public属性成员。其中protected成员有*ht(自定义散列表指针)、*e

    jedis中的redis命令

    Jedis是Java语言实现的Redis客户端库,它提供了一系列方法,方便Java开发者能够操作Redis数据库,执行各种数据库操作。Redis是一种开源的高性能键值存储数据库,广泛应用于缓存、会话管理、消息队列等场景。本文将...

    一些Java编程

    g.drawString("取出的内容:" + myHash.get(m_int1) + "," + myHash.get(m_int2), 100, 120); g.drawString("取出的内容:" + myStack.pop() + "," + myStack.pop(), 100, 140); } catch (Exception e) { g....

    PHP实现普通hash分布式算法简单示例

    在分布式系统设计中,哈希算法是实现数据分布的关键技术之一。在本篇文章中,我们探讨如何使用PHP语言实现一个普通的哈希分布式算法,并通过具体的实例来解释算法的定义与使用技巧。 首先,哈希分布式算法的概念是...

    jedis-jedis-3.2.0.zip

    - 哈希操作:可以使用`hset`和`hget`来操作哈希表,例如`jedis.hset("myHash", "field", "value")`和`String fieldValue = jedis.hget("myHash", "field")`。 - 列表操作:`lpush`用于在列表前端添加元素,`rpop`...

    redis最简单例子

    在实际应用中,你可能还需要了解如何持久化Redis中的数据,如RDB和AOF两种持久化策略,以及主从复制和哨兵系统以实现高可用性。此外,Redis还提供了事务(Transactions)、发布订阅(Publish/Subscribe)和Lua脚本等...

    redis命令实践.docx

    连接 Redis 服务器可以通过 `redis-cli` 命令实现,默认情况下,它会尝试连接到本地主机上的 Redis 服务器(localhost,端口 6379): ```bash redis-cli ``` 一旦成功连接,你将看到一个类似于这样的提示符: ``` ...

    fileman 源码

    `MyHash.cs` 和 `myvars.cs` 可能包含了自定义的哈希计算和全局变量的实现。 综上所述,"fileman" 是一个利用 C# 的强大功能实现的文件管理工具,它利用正则表达式提供了灵活的文件操作,如批量改名、搜索、计算...

    Redis命令实践.pdf

    - **主从复制**:支持主从复制,实现数据备份及读写分离。 - **发布/订阅功能**:可用于构建消息队列系统。 #### 二、连接Redis服务器 要开始使用Redis,首先需要连接到Redis服务器。通常可以通过`redis-cli`工具...

Global site tag (gtag.js) - Google Analytics