`
bearsorry
  • 浏览: 93981 次
  • 性别: Icon_minigender_2
  • 来自: 湖南
社区版块
存档分类
最新评论

用java分析hash表结构及性能(一)

 
阅读更多

   博客开始先发几句牢骚:今天这网速太慢了!我打开这个编辑页面就花了我半个小时!!!!!坑爹啊!还有,这是我第一次是现在word上编辑,再进行复制粘贴的。。。可能在排版上出现一点点问题吧,但我实在没有精力弄这个了,现在只是将哈希表的一些基本概述了一遍,接下来我会对HashMap和HashTable进行对比,找出他们之间的差别!

   好了,开始今天的话题吧!

 

   谈到hash表,对于我们在大二第二学期就学过了数据结构的来说,是有所了解的了,那时候我们是用的C语言,但由于那时候哈希表这一章是放在最后再讲的,随着课程的结束,我们的实验部分也就木有来的及时间去做了,现在重新拿起这些知识,只想感叹一句,理论理解和实践动手操作有着一条鸿沟!

一、数据结构之查找表

在说哈希表之前先简单介绍一下查找表,查找表是一种数据结构,是存放着同类型数据元素的集合,这里主要讲动态查找表。我们对查找表的操作大部分都是查找,但是这里所说的查找不仅仅只是检索的意思,而是包含四个意思:

1、查询某个“特定的”数据元素是否在查找表中

2、检索某个“特定的”数据元素的各种属性

3、在查找表中插入一个数据元素

4、从查找表中删去某个数据元素

特别是在对数据库的操作中,我们有查询、删除、添加等功能,随着数据量的增加,要求我们对数据的处理速度也要不断的提升,我们可以用顺序存储结构的数组,也可以用链式存储结构的链表来对数据进行处理,但数组的插入数据的效率很慢,而链表的查询速度很慢,这里我用实际代码来验证了

 

数组的代码为:

package cn.java1118;
/**
 * 在数组中进行插入和查找
 * @author acer
 *
 */
public class ArrayTest {

	//一个存放整型数据的数组
	public Integer[] array;
	/**
	 * 给出数组大小的构造函数
	 * @param capcity:数组大小
	 */
	public ArrayTest(int capcity){
		array = new Integer[capcity];
		//并将数组初始化
		for(int i=0;i<capcity;i++){
			array[i] = i;
		}
	}
	/**
	 * 在数组下标为index的位置插入一个数据
	 * @param index:下标位置
	 */
	public void insertDate(int index,int Data){
		//看空间是否足够
		if(null==array[array.length-1]){
			System.out.println("空间不足");
			//说明空间不足
			Integer[] oldArray = array;
			array = new Integer[oldArray.length+1];
			for(int j=0;j<oldArray.length;j++){
				array[j] = oldArray[j];
			}
		}else{
//			System.out.println("在这个位置插入一个值:"+index);
			for(int i=array.length-1;i>index;i--){
				if(null!=array[i-1]){
					array[i] = array[i-1];
				}
			}
			array[index] = Data;
		}
	}
	/**
	 * 根据下标取得数值
	 * @param index:下标
	 * @return:对应下标里面的值
	 */
	public int get(int index){
		return array[index];
	}
	/**
	 * 进行测试的主函数
	 * @param args
	 */
	public static void main(String[] args) {
		ArrayTest array = new ArrayTest(100000);
		//开始插入的时间
        Long BeginTime1=System.currentTimeMillis();
        for(int i=0;i<100000;i++){ 
        	array.insertDate(i, i);
        }
        //插入结束的时间
        Long EndTime1=System.currentTimeMillis();
        System.out.println("插入的时间为-->"+(EndTime1-BeginTime1));  
        //开始查询的时间  
        Long BeginTime2=System.currentTimeMillis();  
        array.get(10); 
        //查询结束的时间
        Long EndTime2=System.currentTimeMillis();//记录EndTime  
        System.out.println("查找的时间为--->"+(EndTime2-BeginTime2));
	}
}

   

 

 

 

这是我测试出来的结果:(这还只是数据量为100000的时候,当我用1000000测试的时候,等了好久都不出结果。。。)

插入的时间为-->27645

查找的时间为--->0

 

链表的代码:

 

package cn.java1118;

/**
 * 在链表中进行插入和查找
 * 
 * @author acer
 * 
 */
public class ListTest {

	// 头结点
	public Node head;
	//构造函数
	public ListTest(){
		head = new Node();
		head.data = 0;
		head.index = 0;
	}

	/**
	 * 插入数据
	 * 
	 * @param index
	 *            :下标
	 * @param data
	 *            :数据
	 */
	public void insert(int index, int data) {
		Node newNode = new Node();
		newNode.setData(data);
		newNode.setIndex(index);
		if(null!= head.next){
			newNode.next = head.next;
		}
		head.next = newNode;
	}

	/**
	 * 根据索引值找数据
	 * 
	 * @param index
	 *            :索引值
	 * @return:数据
	 */
	public int get(int index) {
		Node newNode = head;
//		int t=0;
		while (newNode.index != index) {
//			t++;
//			System.out.println("查询的次数为:"+t);
			newNode = newNode.next;
		}
		return newNode.data;
	}

	// 内部类设置链表的节点
	public class Node {
		// 下一个节点
		private Node next;
		// 存放的整型数据
		private int data;
		// 索引的位置
		private int index;

		// set和get方法
		public Node getNext() {
			return next;
		}

		public void setNext(Node next) {
			this.next = next;
		}

		public int getData() {
			return data;
		}

		public void setData(int data) {
			this.data = data;
		}

		public int getIndex() {
			return index;
		}

		public void setIndex(int index) {
			this.index = index;
		}
	}

	/**
	 * 用于测试的主函数
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		ListTest list = new ListTest();
		// 开始插入的时间
		Long BeginTime1 = System.currentTimeMillis();
		for (int i = 0; i < 1000000; i++) {
			list.insert(i, i);
		}
		// 插入结束的时间
		Long EndTime1 = System.currentTimeMillis();
		System.out.println("插入的时间为-->" + (EndTime1 - BeginTime1));
		// 开始查询的时间
		Long BeginTime2 = System.currentTimeMillis();
		for(int i=2;i<100;i++){
			list.get(i);
		}
		// 查询结束的时间
		Long EndTime2 = System.currentTimeMillis();// 记录EndTime
		System.out.println("查找的时间为--->" + (EndTime2 - BeginTime2));
	}
}

 

 

这是我测试出来的结果:(这里的用的链表用了索引,在查找的时候根本没有有去跟每一个值去比较,插入的时候也只是插入到第一个节点的后面,查找的数据量也没有测试很多,但时间就有这么长了,可想而知用链表查询的速度是多慢了。。。)

插入的时间为-->201

查找的时间为--->475

 

为此出现一种复合型的数据结构来弥补这两个缺陷,该结构就是哈希表,也称为散列表。

二、哈希表的基本知识介绍

哈希表是一种数据结构。是一种可以进行快速查询和插入以及删除的数据结构,以前我们进行查找一个数据的时候,一般都要与数组或者链表中的每一个值进行比较,而在哈希表中,利用一个关键字跟数据值绑定在一起形成键值对,再将关键字和哈希地址形成一种映射的关系,关键字作为记录在表中相应的存储位置,这样就可以很快的找到我们想要查询的东东了。

 

哈希表的查找过程如图所示:

 

 

 

 查找分析图

 

 

 

正因为关键字与哈希地址的映射关系这么重要,那么就有下面的问题,这个映射怎么去设置?!这里我们把这样的映射叫做哈希函数,我们可以很清楚的指导哈希函数是一个压缩映象,因此,在一般情况下,很容易产生“冲突”现象,即: key1¹ key2,而  f(key1) = f(key2),冲突问题又该怎样解决呢?!

三、哈希函数

数据中的关键字(key)和该数据在表中的位置存在着一种函数关系,这个函数f(key)就叫做哈希函数。哈希函数其实就是一个映射,是将关键字的集合映射到某个地址集合中

构造哈希函数的方法有以下几种:

1、直接定址法

哈希函数为关键字的线性函数,H(key) = a*key+b;这样的哈希函数中,地址集合的大小=关键字集合大小

2、数字分析法

根据关键字(整型)每一位上的数字出现的频率来决定哈希地址集合的范围,如果某一个位上的数字出现得比较集中(即频率比较高),则哈希地址在这一位上的数字不变,反之,在分散得比较均匀的位上选取的概率大一些。一般这种方法适用的范围是:事先估计出所有关键字每一位上数字出现的频率!

3、平方取中法

以关键字的平方值的中间几位作为存储地址。

4、折叠法

将关键字分割成若干部分,然后取它们的叠加和为哈希地址。

5、除留余数法

哈希函数为:H(key) = key MOD p ( pm ),其中, m为表长, 为不大于 的素数或是不含 20 以下的质因子

6、随机数法

哈希函数为随机函数:H(key) = Random(key)

四、装载因子

 

装载因子λ=装载因子,当装载因子很小的时候就浪费了存储空间,当装载因子很大的时候就容易出现冲突,所以要选择恰当大小的装载因子,这样能提高查找和插入的速度,及提高查找性能!

 

五、解决冲突的办法

1、开放定址法

Hi = ( H(key) + di ) MOD m,其中m为表长,H(key)为哈希函数,di的大小可以自己定,一般是这样的,如果两个key的哈希地址一样,那第二个key在这个哈希地址的周围去寻找没有放值的地址。这里根据增量序列di的大小不一样,分为以下三种方法:

1) 线性探测再散列
      di = c*i   最简单的情况  c=1

 

2) 二次探测再散列
      di = 二次探测…,

 

3) 随机探测再散列
      di是一组伪随机数列或者

   di=i×H2(key) (又称双散列函数探测)

 

2、链地址法

     将所有哈希地址相同的记录都链接在同一链表中

 

 

     例如:

一组关键字(19,14,23,1,68,20,84,27,55,11,10,79),哈希函数为:H(key)=key MOD 13, 用链地址法处理冲突为:

 

 

链地址法

 

 

 

我画的分析图粘贴不上来。。。下次再弄吧。。。)

3、再哈希法

构造若干个哈希函数,当发生冲突时,根据另一个哈希函数计算下一个哈希地址,直到冲突不再发生。

六、性能分析

要知道其实哈希表就像是在数组中存放链表一样的复合型的数据结构,它结合了数组和链表的优点:

1、查找的速度

这里根据哈希函数得到哈希地址,再根据装载因子的大小看,不会像链表一样,全部放在一条链上面,这里是将其分散开来,则查找起来也会快一些。

2、查找所占用的存储空间的大小

由于数据量的增加,再哈希过程是会存在的,然而它是一个动态的变化过程,并不像数组一样可能会占用很多空闲的空间。

3、平均查找长度ASL

ASL,因为有冲突的存在,hash表中的ASL是不可能为1了,主要是根据哈希函数和装载因子的大小而定的!

  

 由于一篇可以放的代码量有限,性能的测试就放在下一章吧。。。。

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论
1 楼 javafound 2011-11-23  
很好,就是排版和图
1.不要从word里直接粘
2.图片上传,再试下

相关推荐

    哈希计算工具 java-hash.7z

    哈希计算工具 `java-hash.7z` 是一个专门用于进行哈希运算的软件包,主要服务于编程领域,特别是Java开发者。哈希计算在信息技术中扮演着至关重要的角色,它能够将任意大小的数据映射为固定长度的哈希值,这个值通常...

    数据结构与算法分析 java语言描述第三版 源代码

    这本书通过使用Java编程语言,向读者展示了如何设计、实现和分析一系列关键的数据结构和算法,以解决实际问题。书中的源代码是理解理论知识与实际应用之间的桥梁,为学习者提供了实践操作的机会。 首先,我们来看...

    打造最快的Hash表(和Blizzard的对话)

    通过上述分析,我们可以看到Blizzard的Hash表实现不仅体现了对散列技术的深刻理解,而且还展示了如何利用多个散列值来进一步提高Hash表的性能和准确性。这种技术尤其适用于处理大量数据的情况,能够显著减少冲突的...

    数据结构与算法分析_Java语言描述(第3版)源码分享

    《数据结构与算法分析_Java语言描述(第3版)》是一本深入探讨数据结构和算法分析的专业书籍,它以Java编程语言为载体,详细阐述了如何高效地组织和操作大量数据,以及如何评估和比较不同算法的性能。这本书不仅涵盖了...

    数据结构与算法分析java语言描述书上源代码

    5. **哈希表** (CuckooHashTable.java): 哈希表是一种基于哈希函数的查找结构,用于实现快速的查找、插入和删除操作。Cuckoo哈希是一种特殊的哈希表设计,通过Cuckoo算法处理冲突,具有较高的空间效率和较低的查找...

    哈希表java实现及简单演示

    哈希表,又称为散列表,是一种数据结构,它通过使用散列函数将键(Key)映射到数组的特定位置来实现快速访问。在Java中,哈希表的实现主要依赖于`java.util.HashMap`类,它是基于哈希表的Map接口实现。在这个Java版...

    Java里多个Map的性能比较(TreeMap、HashMap、ConcurrentSkipListMap)

    在Java编程中,Map接口是用于存储键值对的数据结构,而Java提供了多种Map的实现,包括TreeMap、HashMap和ConcurrentSkipListMap。本文主要比较了这三种Map的性能,尤其是在插入和查找操作上的效率。 1. **TreeMap**...

    一致性hashjava实现

    Ketama一致性哈希算法由Last.fm的工程师开发,其设计目标是优化分布式哈希表的性能,特别是在处理大量小键值对时。它通过引入虚拟节点的概念,提高了哈希空间的分布均匀性,减少了因节点变动导致的数据迁移。 1. **...

    数据结构 课件java版本

    本课件“数据结构 课件java版本”是基于Java编程语言来讲解这一主题的,旨在帮助学生和开发者深入理解数据结构的基本概念,并掌握用Java实现这些数据结构的方法。 在Java中,数据结构主要分为以下几类: 1. **数组...

    Java-codes.rar_HASH JAVA_huffman

    在给定的“Java-codes.rar_HASHJAVA_huffman”压缩包中,包含了多个与Java编程相关的示例代码,主要涉及哈夫曼编码(Huffman Coding)、快速排序(Quicksort)、哈希表(Hash)以及一种名为“q_sort”的排序算法。...

    java描述版数据结构(简单易懂)

    哈希表(Hash Table)是一种通过哈希函数快速定位数据的数据结构,实现了键值对的快速查找。Java中的哈希表主要由`java.util.HashMap`和`java.util.Hashtable`类代表,具有O(1)的平均查找时间复杂度。 六、高级排序...

    数据结构与算法分析_Java语言描述(第3版)源码

    7. **哈希表**(CuckooHashTable.java, CuckooHashTableClassic.java): 哈希表提供了一种快速查找和插入数据的方法,通过散列函数将键映射到数组的索引。Cuckoo哈希是一种特殊的哈希表实现,使用了Cuckoo算法,当冲突...

    10种java性能优化方案.docx

    - **选择高效的算法和数据结构**:例如,使用哈希表而不是链表进行查找操作。 - **避免不必要的计算**:如文档中提到的,优化算法的关键部分,特别是那些占总运行时间较大比例的部分。 - **利用缓存技术**:通过...

    uthash开源的hash函数实现

    UTHASH 是一个开源的 C 语言库,提供了一种简单且高效的哈希表实现,用于在 C 代码中快速查找和管理数据结构。这个库的主要功能是提供一个宏定义的集合,可以方便地将结构体转化为哈希表,进而进行添加、删除、查找...

    数据结构与算法分析-java

    标题《数据结构与算法分析-java》说明这是一本专注于Java语言的数据结构和算法的教科书。本书由Mark Allen Weiss所著,目的在于通过Java语言来分析和探讨数据结构及算法的原理与应用。根据提供的部分目录内容,本书...

    PersistentIdealHashTree-Java实现

    这种数据结构是一种基于哈希表的持久化数据结构,其核心特点是能够在保持旧版本的同时添加新数据,即具备了版本控制的能力。这使得它在数据库系统、分布式存储和多版本并发控制等领域有着广泛的应用。在Java中实现这...

    数据结构与算法分析(Java版)

    《数据结构与算法分析(Java版)》是由Robert Lafore编写的一本详细介绍数据结构与算法原理及其实现的书籍。本书旨在帮助读者理解如何通过数据结构和算法解决实际问题,并通过Java语言进行实践。 #### 二、数据结构...

    Hash map 哈希表

    哈希表,也被称为Hash Map,是计算机科学中一种高效的数据结构,用于存储键值对。它通过一种称为哈希函数的过程将键映射到数组的特定位置,从而实现快速的查找、插入和删除操作。在哈希表中,查找时间通常接近常数...

Global site tag (gtag.js) - Google Analytics