只言片语:
哈希表是一个神奇的东西,为了让它相当神奇,我付出了神奇的努力,终于可以拿出来见人了。毕竟,这东西就拿来比较的,在此是和系统的哈希map来比较的。
设计思路:
在这里,自己实现的哈希map融合了链表和数组两者,也就是说,融合了数组查找方便、链表删除简便的同时也融合了链表遍历复杂,浪费时间,,以及数组删除麻烦的缺点;但是,总的来说,这里扬了长避了短;最大程度的使得整个程序的性能达到一个比较高的层次;
其实,我们主要是用数组来存储链表的,应该具体的说是,一个数组的每个索引位置都存放着一个链表的头结点,通过头结点就可以获取它的子节点,当然此数组的数据类型是链表型的。用图形来阐述这种思想的话,如图所示:
而每个节点的话,里面存放的是我们的用户对象;用户对象里面就包含了用户的相关信息;
在此,定义了四个类;
1.Item :
此类的其实就是一个用户类;
里面包含了用户相关的属性和方法;如:用户名,密码;
2.LinkNode :
这个类就是链表的节点类;
含有Item数据类型的属性。
3 HashMap :
我们真正要实现的一个类;
里面含有存放,获取,删除,以及当装载因子达到一定的值得时候,将原先的数据重新第一一个二倍原先数组长度的数组,进而在此通过哈希函数来在此确定老数据在新数组里面的索引位置;
4 TestMain 类;
用来测试我们自己定义的哈希map。
其实,整个的过程就是我们将一个Item类型的对象给其赋值等操作,,然后,将这个Item对象作为LinkNode类的属性值来将其绑定和LinkNode类型的对象;再将其LinkNode对象的给数组,如果数组的索引位置是非空的,那么我们将这个要放入数组中的节点指向数组中原本存放的节点,然后将此节点赋值给此数组索引位置,通俗点来说,就是每次将要放得节点当成一个头结点来放入数组中,而数组中原先的节点全都往后移。这样在置放节点的时候就可以大大的降低时间复杂度,提高系统的性能,否则的话,要每次放入新节点的时候还要遍历原先对应索引位置的链表找出最后一个节点。然后将其加到最后的节点后面,这本身就是一件非常复杂的事情。这是我们在设计的时候就应该不避免的。
这里有些东西需要实现说明一下:
装载因子 loadfactor :
对于一个指定的数组来说,当它里面每次置放的时候索引位置的数值从null变成!Null的过程,计数器count从0开始增加,每从null变为!Null的时候count加一。Count和数组的总长度length的比值就是装载因子,各个系统的装载因子一般是不一样的,在java里面一般是0.75。我们在置放的函数调用的时候要进行判断,当loadfactor大于等于0.75的时候,那么要进行ReHash();
哈希函数 HashCode();
在这里的目的是为了得到某个节点在数组中存放的位置index。一般情况下我们拿用户对象的某个属性值来计算。比如说,在此,我们使用用户的用户名模数组的长度来实现的。这里username是整形的变量。
即索引位置是index =username%length;
当用户的某个属性为非数字类型的时候,比如说这里的用户名是String类型,那么我们可以调用系统的username.Hashcode();得到,然后再除以数组的长度取模来得到索引的位置。
代码专区:
1.Item类:
package cn.netjava2;
public class Item {
private int username; // 账户定义为整形,方便我们哈希函数的操作,当然可以根据自己的需要定义成String类型也好。
private String password;// 定义一个密码变量;
public void setUsername(int username) {
this.username = username;
}
public int getUsername() {
return username;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return this.password;
}
}
2.LinkNode类
package cn.netjava2;
public class LinkNode {
private Item it; // 定义一个Item数据类型的变量;
private LinkNode NextNode;//节点的下一个值;
public void setItem(Item it) {// 给Item类型的变量赋值
this.it = it;
}
public Item getItem() { // 获取Item变量类型的值;
return this.it;
}
// 定义一个哈希函数;
public int HashCode(int username, int length) {
int index = username % length;
return index;
}
// 将当前的node值赋给下一个节点,让其引用;
public void setLNextNode(LinkNode node) {
NextNode = node;
}
// 返回下一个节点;
public LinkNode getNextNode() {
return this.NextNode;
}
}
3.HashMap 类;
package cn.netjava2;
public class HashMap {
private LinkNode[] Lnode; // 我们的目标是要将所存放的数据依次进行封装,然后依次放入
// 这个实现的过程是,先将Item用户对象进行封装,放置在节点LinkNode 对象里面,然后把LinkNode 节点的首节点,也就是根节点放入到
// 数据类型为节点LikNode 类型的数组中;
private static int length = 20; // 数组的初始化长度;
private static int count = 0; // 当数组里面的空间位置每被占用一个,那么数组苏勇的空间就加一;
// 当我们每次实例化HashMap对象的时候,我们就应该让它同时生成一个节点类型的数组;
public HashMap() {
Lnode = new LinkNode[length];
}
public void Put(LinkNode node) { // 将一个已经封装好Item对象的节点放入节点数组l里面;
// 获取置放数组的索引位置;
int index = node.HashCode(node.getItem().getUsername(), length);
LinkNode onenode = Lnode[index];
// 判断此索引位置的情况;
if (onenode == null) {// 当为空值的时候,直接放入,此节点则为根节点;此时,数组的空间被占用了一个,数组的空间计数器则加一;
Lnode[index] = node;
count++;
if (loadSize(count)) {
ReSize();
}
} else {
// 将数组里面的根节点值,赋给下一个,而将我们要添加的节点node放入数组中,充当根节点的角色;
// 这样做的好处是,不用添加的时候编遍数组索引对应的节点。再添加到末节点,降低时间复杂度,更好的提高系统执行的性能,方便用户;
node.setLNextNode(onenode);
Lnode[index] = node;
}
}
// 得到我们所需要的值;比如说,通过一个用户名来得到存放在其中的密码值或者其它信息与用户相关的;
public String getPassword(int username) {
LinkNode node = new LinkNode();
int index = node.HashCode(username, length);
LinkNode lnode = Lnode[index];
String password = "";
while (lnode != null) {
// 从次索引对应的节点中得到Item用户对象,已经对户对象的用户名;如果相同则返回此用户名所对应的密码;
if (lnode.getItem().getUsername() == username) {
password = lnode.getItem().getPassword();
}
lnode = lnode.getNextNode();
}
return password;
}
// 删除一个节点,来清空一个我们不需要了的用户对象;
public void DeleteLNode(int username) { // 通过用户名来删除节点;
// 调用哈希函数来获取索引的位置;
LinkNode node = new LinkNode();
int index = node.HashCode(username, length);
LinkNode rootnode = Lnode[index];
while (rootnode != null) {
LinkNode nextnode = rootnode.getNextNode();
if (nextnode.getItem().getUsername() == username) {
rootnode = nextnode.getNextNode();
nextnode = null;// 如果此节点为需要删除的节点,那么将此目标节点的前一个节点指向目标节点的后一个节点,完成删除过程;
// 同时将此节点清空;
if (rootnode == null) {
Lnode[index] = null;
count--;
} else {
Lnode[index] = rootnode;
}
} else {
//
nextnode = nextnode.getNextNode();
}
}
}
// 当数组里面的存储量超过一定的范围的时候,那么我们需要对其进行扩充数组的操作;
public void ReSize() {
// 建立一个新的数组;容量为前一个数组的两倍;z再次操作之前,我们需要对数组的容量置零;
count = 0;
LinkNode[] newLnode = new LinkNode[length * 2];
// 遍历原先的数组,取出其中的值,然后放入新的数组之中;
for (int i = 0; i < Lnode.length; i++) {
LinkNode node = Lnode[i];
LinkNode nextnode;
while (node != null) {
nextnode = node.getNextNode();
if (nextnode != null) {
int username = nextnode.getItem().getUsername();
int index = nextnode.HashCode(username, length * 2);
if (newLnode[index] == null) {
newLnode[index] = nextnode; // 如果要置放的新数组索引没有被占用,则直接放在数组所有的位置
} else {
LinkNode Node = newLnode[index]; // 如果已经被占用了,那么将这个节点从数组中取出来;,放到要放入节点的后面;
nextnode.setLNextNode(Node);
newLnode[index] = nextnode;
}
}
node = nextnode;
}
}
length = length * 2;
Lnode = newLnode;
}
// 装载因子的判断;
public boolean loadSize(int count) {
boolean value = false;
int load = count / length;
if (load >= 0.75) {
value = true; // 当因子值是真的时候,说明此时,应该扩展空间;
}
return value;
}
}
4.TestMain 类;
package cn.netjava2;
import java.util.Date;
public class MainTest {
public static void main(String[] args) {
MainTest mt = new MainTest();
// 调用系统提供的;
int pvalue = 1000;
int gvalue = 300;
// mt.TestSystemHashMap(pvalue,gvalue);
System.out.println();
// 调用自己实现的哈希map;
mt.TestMyHasgMap(pvalue, gvalue);
}
// 测试自己的HashMap的方法;
public void TestMyHasgMap(int pvalue, int gvalue) {
HashMap hm = new HashMap();
// 测试10000组值;
int key1 = pvalue;
Date d1 = new Date();
long n1 = d1.getTime();
while (pvalue-- > 0) {
Item it = new Item();
it.setUsername(pvalue);
it.setPassword(pvalue + "");
LinkNode node = new LinkNode();
node.setItem(it);
hm.Put(node);
}
Date d2 = new Date();
long m1 = d2.getTime();
// javax.swing.JOptionPane.showMessageDialog(null,
// "MyHashMap置放"+key+"个数据用的时间是:"+(m-n)+"毫秒");
System.out.println("MyHashMap置放" + key1 + "个数据用的时间是:" + (m1 - n1)
+ "毫秒");
int key2 = gvalue;
Date d3 = new Date();
long n2 = d3.getTime();
while (gvalue-- > 0) {
System.out.println("密码 " + hm.getPassword(gvalue));
}
Date d4 = new Date();
long m2 = d4.getTime();
// javax.swing.JOptionPane.showMessageDialog(null,
// "MyHashMap置放"+key+"个数据用的时间是:"+(m-n)+"毫秒");
System.out.println("MyHashMap获取" + key2 + "个数据用的时间是:" + (m2 - n2)
+ "毫秒");
}
// 测试系统HashMap的方法;
public void TestSystemHashMap(int value1, int value2) {
java.util.Map<String, String> Hmap = new java.util.HashMap<String, String>();
int key1 = value1;
Date d1 = new Date();
long n = d1.getTime();
while (value1-- > 0) {
Hmap.put(value1 + "", value1 + "");
}
Date d2 = new Date();
long m = d2.getTime();
System.out.println("SystemHashMap置放" + key1 + "个数据用的时间是:" + (m - n)
+ "毫秒");
// javax.swing.JOptionPane.showMessageDialog(null,
// "SystemHashMap置放"+key+"个数据用的时间是:"+(m-n)+"毫秒");
int key2 = value2;
Date d3 = new Date();
long n1 = d3.getTime();
while (value2-- > 0) {
Hmap.get(value2 + "");
}
Date d4 = new Date();
long m1 = d4.getTime();
System.out.println("SystemHashMap获取" + key2 + "个数据用的时间是:" + (m1 - n1)
+ "毫秒");
}
}
*******************************************************************************
测试结果:
有图有真相。嘿嘿!
单独测试一个获取密码的函数。我讲系统的和自己的对比。因为打印很多的缘故先取少点。我们在put的时候,密码和用户相同,我们来测试一下,是不是可以得到username为1-25之间的用户密码是不是为0-25;此取值限于打印篇幅的缘故。
来看系统的
这样看起来系统的要少一点,可是有一个比较重要的原因,那就是我们打印时需要时间的。将打印语句去掉之后,我们再看
是不是一样了。从上述的多组测试中,我们就可以看出来,自己实现的哈希map相对要性能高一些。并且,当测试的数据上百万的时候,我们自己的hashmap换可以承受,但是系统提供的就崩溃了。看图:
这是我们自己的hashmap实现的。依然可以正常运行。且看。系统的
具体效果请看:
http://blog.sina.com.cn/s/blog_67ac56e70100yhnp.html
分享到:
相关推荐
HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,虽然它们实现的接口规范不同,但它们底层的 Hash 存储机制完全一样。甚至 HashSet 本身就采用 HashMap 来实现的。 2. Hash 存储机制 HashMap ...
Java 实现门禁系统是 Java 语言实现的门禁系统,通过 Java 语言实现门禁系统的功能设计和实现方法。该系统主要包括电子门管理、身份验证和门禁控制三个部分。下面是该系统的知识点总结: 门禁系统的设计 门禁系统...
【铁路订票系统JAVA实现】是一个面向初学者和中级JAVA开发者的学习资源,旨在提供一个实际的项目案例,帮助他们深入理解和应用JAVA编程语言。这个系统是基于JAVA技术栈构建的,能够模拟真实的在线订票流程,包括用户...
HashMap是Java编程语言中的一种内置数据结构,它提供了O(1)的平均时间复杂度来执行查找、插入和删除操作,这使得它成为构建词典查询系统的理想选择。 首先,我们来深入理解HashMap的工作原理。HashMap基于哈希表的...
【Java实现的考试系统】是一种基于Java编程语言开发的在线教育平台,主要用于组织、管理和进行各种类型的考试。这个系统包含了多个关键模块,旨在提供全面的考试管理服务。 1. **添加试题**:此功能允许管理员或者...
在这个系统中,使用HashMap实现是一种高效且灵活的方法,因为HashMap提供了快速的查找和插入操作。本文将深入探讨如何使用HashMap来构建一个电话本管理系统,并通过源码分析增强理解。 HashMap是Java集合框架中的一...
Java银行管理系统的实现是软件开发领域的一个典型应用,它涵盖了多方面的编程技术和设计模式。在这个系统中,"增删改查"(CRUD)是最基本的功能,分别对应于创建(Create)、读取(Read)、更新(Update)和删除...
### Java使用WebService读取HashMap里的数值 #### 背景介绍 在Java开发中,`WebService`是一种常用的技术栈,用于实现不同系统间的通信...对于开发者而言,理解和掌握这一技术是构建稳定可靠的分布式系统的基石之一。
本项目“JAVA实现学生信息管理系统+图形用户界面(GUI)”是利用Java技术实现的一个实用系统,它结合了后端逻辑处理和前端用户交互,为管理学生信息提供了一个直观、便捷的平台。以下将详细介绍该系统的相关知识点: ...
"基于HashMap的用户标签处理兼Java中HashMap实现原理研究" 本文研究了基于HashMap的用户标签处理方法,并对Java中HashMap的实现原理进行了深入研究。HashMap是一种高效的数据结构,可以快速地存储和检索数据。本文...
通过学习和分析这个项目,开发者可以掌握如何用Java实现一个简单的信息管理系统,并了解上述技术在实际项目中的应用。这个项目对于初学者来说是很好的实践,对于有经验的开发者则是一个加深对Java和数据库操作理解的...
在本项目中,“自动售货机系统JAVA实现”是一个基于Java编程语言的软件系统,用于模拟和控制实际的自动售货机操作。这个系统可能是为了一个硬件课程设计而创建的,目的是让学生理解如何将编程技术应用于实际设备的...
1. **Java基础**:首先,实现任何Java小系统都需要扎实的Java基础知识,包括语法、面向对象编程(OOP)概念、类和对象、封装、继承和多态等。理解异常处理、集合框架(如ArrayList、LinkedList、HashMap等)、输入/...
在开发“Java实现标准化考试系统app”时,我们需要掌握一系列Java技术及数据库管理知识。以下是对这个项目中涉及的关键知识点的详细说明: 1. **Java编程基础**:Java是一种广泛使用的面向对象的编程语言,其跨平台...
《Java实现的学生管理系统详解》 在信息技术领域,学生管理系统是一个常见的应用系统,它主要用于高校、培训机构等教育机构,便于管理学生的个人信息、成绩、出勤等数据。本项目以Java为开发语言,结合ACCESS数据库...
在本项目中,"Java实现汽车租赁系统"是一个旨在教授初学者如何利用Java编程语言构建一个基础的汽车租赁管理软件的教程。这个系统的核心功能可能包括汽车信息管理、租赁服务处理、用户账户管理以及基本的数据存储和...
本篇文章将深入探讨如何使用Java实现一个包含基本功能如`select`、`insert`(插入后排序)、`create`和`delete`的数据库系统。 首先,我们需要理解数据库系统的基本架构。一个简单的数据库系统通常包括以下组件: ...
在本资源中,我们将学习如何使用 Java 语言实现一个简单的购物车系统,其中使用 HashMap 来存放用户想买的商品信息。下面是该资源中的知识点总结: ConnDB.java ConnDB.java 是一个用于连接数据库的类,该类中...
文件名“2015-12-JAVA实现微型超市管理系统”暗示了项目可能包含了多个源文件和资源文件,如Java类文件、配置文件、数据库脚本等。开发者需要理解如何组织这些文件,以及如何通过IDE(如Eclipse或IntelliJ IDEA)来...
HashMap是Java集合框架的一个重要组成部分,它实现了Map接口,能够存储键值对映射。在Java编程语言中,最基本的数据结构有两种:数组和引用(模拟指针)。所有的复杂数据结构都可以基于这两种基本结构构建出来,...