Hash表实现的意义
作为数据类型的一种,Hash表做到了数组和链表两类基本数据类型的完美结合。Hash表继承两者的优点,让我们在做数据处理方面有了很多的方便,不但是时间上的还有空间上的。在数组中我们知道,存放的数据与及记录的关键字上没有太多的关系,很多都只是一个随机的关系。那么这样在我们进行相应的操作时,会大大的减少我们的工作效率,主要是因为找数据时会进行一系列的比较,这样消耗的比较的次数太多时花费的时间就会变多,自然而然效率就这样被削弱了。所以最最理想的情况就是我们能直接通过他们建立的关系,直接的找到结构中的位置来获取数据。
关系怎么建立呢
现在我们的主要任务就是找到方法来建立对应的关系。这里的方法其实有很多种。不同的数据采用的方法不同。主要有数据分析法、平均取中法、取余法、随机数等等方法。在这里不做具体的介绍。所以今天我在自定义自己的Hash表时,用的是JDK中提供的hashCode()方法。有兴趣的还可以相应的拓展。
Hash表到底是什么
真的是真的,听了我说上面的你也许还没有理解Hash表是个什么东东。一句话:就是以链表为元素的数组对象。在我们将数据放在这个所谓的数组对象时,有两种情况:1:原来数组在该下标已经有元素存在了,那么要放入的节点如果经过hashCode()及indexFor()方法计算过后他的下标也是一样的,那么我们采用挂链法将这个节点放到最后;2:如果没有,那么放到对应的index下标中,作为根节点。
最清楚的Hash表代码解释
说了这些肯定也不会马上理解,没事,我们来看代码。
为了大家能够看的清楚明了,这里我建立三个类:一个是链表类、一个是Hash表、一个是测试类
里面的方法都有详细的介绍,主要的是把思路理清。
链表Entry类:
package 哈希表1029; /** * 链表 * @author 祝侦科 * */ public class Entry<K,V> { Entry<K,V> next; K key; V value; int hash; public Entry(K key,V value,int hash){ this.key = key; this.value = value; this.hash = hash; } }
哈希表类:
package 哈希表1029; /** * 自定义的哈希表 * @author 祝侦科 * */ public class Hash<K,V> { private int size;//链表数组的大小 private static int INIT_CAPACITY = 16;//默认的容量,即数组的大小 private static float LOAD_FACTOR = 0.75f;//默认的装载因子 private int max;//能存的最大的数 private Entry<K,V>[] entryArray;//链表数组 public Hash(int capacity,float factor){ this.INIT_CAPACITY = capacity; max = (int)(capacity*factor); entryArray = new Entry[capacity]; } public Hash(){ this(INIT_CAPACITY, LOAD_FACTOR); } /** * 根据hashcode及数组的容量来计算出该哈希码的下标 * @param hashcode hash值 * @param arraySize 数组长度 * @return 下标 */ public int indexFor(int hashcode,int arraySize){ return hashcode & (arraySize-1); } /** * 扩容以减少冲突阀值 * @param arraySize:扩容后新数组的容量 */ public void reHash(int arraySize){ //创建扩容后的数组 Entry<K,V>[] newArray = new Entry[arraySize]; max = (int)(arraySize*LOAD_FACTOR); for(int i=0;i<entryArray.length;i++){ //取得每一个链表对象 Entry<K,V> entry = entryArray[i]; while( null != entry){//遍历链表的每个节点,并且将节点加到新的数组中 this.setEntry(entry, newArray); entry = entry.next; } } entryArray = newArray;//将新的数组赋给原来的数组 } /** * 将节点放到链表数组中 * @param temp:要放入的节点 * @param entryArray:放到这个数组中 * @return:没有完全相同的则返回true,否则false */ public boolean setEntry(Entry<K,V> temp,Entry[] entryArray){ int index = this.indexFor(temp.hashCode(), entryArray.length);//获取要放入的节点的下标 Entry<K,V> entry = entryArray[index];//得到对应的链表对象 if(entry != null){//在while循环中遍历这个链表 while(null != entry){//遍历整个链表是为了发现是否有相同的节点 if((temp.key == entry.key || temp.key.equals(entry.key)) &&(temp.value == entry.value || temp.value.equals(entry.value)) && temp.hash == entry.hash){ return false;//这里要注意比较的方法。。要两种情况都比较 } //只有key相同时,value不同时 else if(temp.key == entry.key && temp.value != entry.value){ entry.value = temp.value; return true; } //当key不同时,则比较下一个元素 else if(temp.key != entry.key){ if(null == entry.next){ break; } entry = entry.next; } } //如果没有发现相同的话,则挂在最后面 this.addLastEntry(entry, temp); return true; } //当entry为null时,将元素放在最开始 this.addFirstEntry(temp, entryArray, index); return true; } /** * 将数据存到Hash表中 * @param k * @param v * @return */ public boolean put(K k,V v){ int hash = k.hashCode();//首先计算出hash值 Entry<K,V> entry = new Entry(k,v,hash); //将entry加到数组中 if(setEntry(entry, entryArray)){ size++; return true; } return false; } public V get (K k){ //先得到下标 int hash = k.hashCode(); int index = this.indexFor(hash, entryArray.length); //得到链表 Entry<K,V> entry = entryArray[index]; if(entry == null){ return null; } //遍历整个链表。。比较k值是否相等。。相等则返回对应的value while(null == entry){ if(entry.key == k || entry.key.equals(k)){ return entry.value; } entry = entry.next; } //不等的话则返回null return null; } /** * 要放的节点要放在某个数组元素(未有元素)中时,将节点设为根节点 * @param temp:要放入的链表 * @param entryArray:链表放入的数组 * @param index:链表在数组中的下标 */ public void addFirstEntry(Entry<K,V> temp,Entry[] entryArray,int index){ if(size > max){//如果容量超标,则扩容 this.reHash(entryArray.length*2); } entryArray[index] = temp;//将链表放在指定的下标中 temp.next = null;//temp是根节点,后面什么都没有了 } /** * 挂链法:将加点挂到相应节点的后面 ***** !!!!!第二个节点参数在后面的!!!!!****(这个顺序要注意!!) * @param entry:要挂在这个节点的后面 * @param temp:要挂的节点 */ public void addLastEntry(Entry<K,V> entry,Entry<K,V> temp){ if(size >max){ reHash(entryArray.length); } entry.next = temp;//挂在后面 } }
最后是测试类:
package 哈希表1029; public class HashTest { /** * @param args */ public static void main(String[] args) { new HashTest().test(args); } public void test(String[] args){ Hash<String ,String> hash = new Hash<String,String>(); long beginPut = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ hash.put("", ""+i); } long endPut = System.currentTimeMillis(); System.out.println("存放数据用时:"+(endPut-beginPut)); long beginGet = System.currentTimeMillis(); hash.get(""+10000); long endGet = System.currentTimeMillis(); System.out.println("获取数据用时:"+(endGet-beginGet)); } }
随后最后运行的结果是:
希望我的讲解能给大家的Hash表的学习带帮助,也希望大家能够多多给我提建议,大家共同进步!
附件:
Hash表源代码包
相关推荐
由于没有详细内容,我们无法深入讨论,但可以推测这可能包含了哈希表的创建、插入、查找和冲突解决等基本操作的代码示例,或者是针对特定场景优化哈希表性能的方法。 总的来说,理解哈希表的工作原理和特性,掌握其...
### 哈希表基础及代码详解 #### 一、哈希表概述 **哈希表**(Hash Table)是一种非常高效的数据结构,其主要优势在于能够极大地减少数据的存储和查找时间,甚至可以近似认为是常数时间复杂度。这种特性使其成为...
### 哈希表的应用详解 #### 一、引言 近年来,在计算机科学与编程竞赛领域,哈希表(HashTable)作为一种高效的存储结构逐渐受到广泛关注。哈希表能够显著提高数据存储与查找的速度,尤其在大数据量处理时,其优势...
**libevent哈希表详解** libevent是一个高度可扩展的事件通知库,广泛应用于网络编程,如服务器端的开发。它允许程序员注册文件描述符、信号、时间等事件,并在这些事件发生时得到通知。在libevent的核心实现中,...
### 哈希表的数据结构详解 #### 一、引言 哈希表是一种非常高效且常用的数据结构,它能够实现快速查找、插入与删除操作。哈希表结合了数组和链表的优点,使得在大多数情况下,其平均时间复杂度接近O(1),即常数时间...
1. **初始化哈希表**:为哈希表分配空间,并将其每个位置的链表初始化为空。 2. **插入元素**:计算元素的哈希值,然后将元素插入对应的链表中。 **查找操作**: 1. **计算哈希值**:根据键值计算哈希值。 2. **...
哈希表(HashTable)在C#编程语言中是一种常用的数据结构,它允许高效地存储和检索键值对数据。在.NET Framework中,`System.Collections`命名空间提供了`HashTable`类,用于存储和管理这些键值对。下面我们将深入探讨...
**项目简介:**本项目旨在开发一个基于哈希表技术的员工信息管理系统,能够高效地处理员工信息的录入、删除、查找、修改等操作,并支持数据的导入和导出功能。 #### 二、关键技术选型 **技术栈:** - **C语言:** ...
根据提供的文档信息,本文将详细解释哈希与哈希表的基本概念、原理及其应用,并着重于字符串哈希的实现方式及其实现中的注意事项。 ### 一、哈希算法概述 哈希算法是一种广泛应用于计算机科学的数据处理技术。它...
示例:将哈希表`my_hash`的字段`field1`的值设为`value1`。 ``` HSET my_hash field1 value1 ``` 2. **HGET**:返回哈希表中指定字段的值。 ``` HGET key field ``` 示例:获取哈希表`my_hash`中字段`field...
### Redis常用语法命令及使用示例详解 #### 一、Redis简介 Redis是一个开源的、内存中的数据结构存储系统,以其高性能和丰富的功能而著称。它可以被当作数据库、缓存以及消息中间件来使用。Redis支持多种数据类型...
### Oracle 分区表详解 #### 一、Oracle 分区简介 Oracle 的分区技术是一种用于管理和优化超大型表和索引的有效手段。通过将一个大型的表或者索引分割成多个较小且可管理的部分,分区技术能够显著提升数据库的性能...
在计算机科学中,哈希函数被广泛应用于各种数据结构中,如哈希表(HashTable),用以加速数据查找的速度。一个好的哈希函数需要具备以下特点: 1. **快速计算**:计算哈希值的过程应该足够快。 2. **均匀分布**:...
通过分析这些文件名,我们可以推测这些代码示例可能覆盖了数据结构(如链表、哈希表)、算法(如哈希、动态规划、组合优化)以及一些特定问题的求解方法。深入学习并理解这些代码可以帮助提升编程技能,尤其是对于...
### 哈希表(Hashtable)的使用及自定义排序详解 #### 一、哈希表简介 哈希表(Hashtable)是一种数据结构,它通过一个哈希函数将键(Key)映射到表的一个位置来访问记录,这加快了查找记录的速度。哈希表在.NET ...
"libcuckoo" 是一个基于 C++ 编写的库,其核心功能在于提供高效的哈希表数据结构,而在 "libcuckoo-windows" 中,这个库被优化以适应 Windows 平台的需求,确保在多线程环境下能够实现高并发性能。 **描述详解:** ...
2. **哈希表的应用**:题目中提到使用哈希表,这表明可以通过构建哈希表的方式来快速查找和统计字符串中的字符出现频率。 3. **排序算法**:题目中提到了排序的时间复杂度要求,这意味着需要实现一种高效的排序算法...
### Oracle表分区详解 #### 1. 表空间及分区表的概念 - **表空间**: - **定义**:表空间是Oracle数据库中的逻辑存储单元,由一个或多个数据文件组成,用于存储数据库对象(如表、索引等)。在逻辑上,表空间为...
- **HDEL**:删除哈希表中的一个或多个指定字段。 - 示例:`HDEL u2 age` ##### 4. 列表类型(Lists) - **定义**:列表是按照插入顺序排序的字符串集合。 - **操作命令**: - **LPUSH**:在列表头部添加一个或多...
这个例子中,我们假设了一个 `finance_enewsuser` 表,用户数据被插入到这个表中。在实际应用中,你应该根据自己的数据库结构进行调整。 在重构用户注册过程中,你可能会遇到一些其他问题,比如处理错误消息、发送...