`
王树雄
  • 浏览: 239551 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

HashMap之java实现和系统比较

 
阅读更多
只言片语:
哈希表是一个神奇的东西,为了让它相当神奇,我付出了神奇的努力,终于可以拿出来见人了。毕竟,这东西就拿来比较的,在此是和系统的哈希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
分享到:
评论

相关推荐

    Java实现和维护系统详解.pdf

    但如果循环是为了找到特定元素,那目前还没有什么优化的办法,使得遍历数组和采用HashMap 的版本一样快。以数据库的性能为例,但运行环境的任何部分都可能会引起性能问题。 对于整体系统,采取结构化方法针对系统的...

    使用Java实现安全的登录系统

    这个代码是一个使用Java实现的安全登录系统的示例。在这个系统中,我们使用了一个`HashMap`来存储用户名和密码的对应关系。在用户输入用户名后,我们首先检查这个用户名是否存在于`HashMap`中。如果存在,则提示用户...

    JAVA实现基于知识图谱的古诗词智能问答

    JAVA实现基于知识图谱的古诗词智能问答 使用java+ssm+springboot+maven+react+mysql 1、前端接收问句,发送至后端(涉及CSS、ajax通信) 2、对问句进行分词,每个单词标注词性(涉及用户字典) 3、对问句进行抽象...

    学生管理系统(java实现)

    学生管理系统,使用ACCESS数据库,实现数据的添加,修改,删除,查询

    Java面向对象吃货联盟项目 (HashMap)

    根据Java面向对象吃货联盟项目修改为通过HashMap存储菜品和订单(其实ArrayList更简单) 实现的功能:订餐,查看餐袋,签收订单,删除订单,我要点赞,退出系统 定义的类:菜品类、订单类、测试类(可以把里面实现的...

    02_Java实现的终端名片管理系统.rar

    没有积分的话,请加v:lanyotach2008, 免费赠送 项目简介: 该系统主要在终端环境下运行,主要有名片的新增、查看、搜索、修改、删除等功能。 可以存储员工名片的姓名、电话、QQ、邮箱和...CardTools.java:功能实现

    Java高级面试第二套1.面试必考之HashMap源码分析与实现

    微信小程序详细图文教程 泉州大白网络科技 ...2.成本3元(腾讯云支持微信小程序2017年推广期间,3元腾讯云提供整套服务器和系统) 3.腾讯云默认分配:1.云服务器;2.云数据库;3.域名;4.小程序支持系统; 4.只要

    基于JAVA的网络通信系统的毕业设计,该系统将实现客户端和服务器之间的通信,并支持多用户同时在线

    本毕业设计旨在研究和开发一个基于JAVA的网络通信系统。该系统将实现客户端和服务器之间的通信,并支持多用户同时在线。以下是本设计的具体内容和代码实现。 ## 系统功能 该系统将具有以下功能: - 客户端和...

    最新Java面试题视频网盘,Java面试题84集、java面试专属及面试必问课程

    │ Java面试题11.HashMap和HashTable的区别.mp4 │ Java面试题12.实现一个拷贝文件的类使用字节流还是字符串.mp4 │ Java面试题13.线程的实现方式 怎么启动线程怎么区分线程.mp4 │ Java面试题14.线程并发库和线程池...

    java7hashmap源码-Java-:Java-

    hashmap源码 Java-# JAVA面试知识梳理 0. java大纲 一、java基础 1.基础 1.1 JDK & JRE 区别 JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。 JRE:Java Runtime ...

    java7hashmap源码-knowledge-juc:知识-juc

    java7 hashmap源码

    Java面试题合集最新版2024.zip

    Java面试通常涵盖多个方面,包括Java基础知识、编程技能、问题解决能力,以及对Java生态系统和相关技术的理解。以下是一些建议的Java面试准备要点和资源描述: 一、Java基础知识 数据类型、变量与运算符:理解Java...

    用java编写的简易的网吧计费系统主要是体现线程和触发器的使用

    用java编写一个简易的网吧计费系统,用hibernate框架,sql2008数据库或者是2005数据库,用户sa密码123456,在数据库中用到触发器,在程序中使用线程来计算钱 实现的主要功能是练习线程、hashmap、hibernate、全局...

    JAVA面试题最全集

    简述 Java Server Page 和 Servlet 的联系和区别。 33.简述synchronized和java.util.concurrent.locks.Lock的异同 ? 34.EJB规范规定EJB中禁止的操作有哪些? 35.java除了8种基本类型外,在虚拟机里还有哪一种,...

    JAVA高并发高性能高可用高扩展架构视频教程

    JAVA企业级基础课题(HashMap那些事) 企业架构师必备技能(JAVA核心技术反射) JavaWeb之基础(手写实现Tomcat服务器) java多线程编程 纯手写实现SpringIOC实现过程 JEE企业级开发(企业级项目开发权威指南) 网络爬虫之...

    java7hashmap源码-java-skill:JavaSkill,Java技能

    JVM是运行在操作系统之上,它与硬件没有直接的交互。 (2)运行过程 ​ 我们都知道Java源文件,通过编译器,能够产生相应的.class文件,也就是字节码文件,而字节码文件有通过Java虚拟机中的解释器,编译成特定机器上...

    java编程宝典

    4.4 如何使用jdk8的新特性LocalDate和LocalDateTime 31 4.5 时间戳 32 6网络 33 Jsoup介绍: 34 Jsoup主要有以下功能: 34 Demo1:抓取校园网首页的新闻标题 35 //从目标页面下载所有图片到本地 36 7异常 45 2.Java...

    java jdk实列宝典 光盘源代码

    java为数据结构中的映射定义一个接口java.util.Map,有四个实现类HashMap Hashtable LinkedHashMap TreeMap用法和区别;对Map排序; 5字符串 使用String;判断一个字符串是否是合法的java标识符;使用StringBuffer;...

    疯狂JAVA讲义

    7.6.1 HashMap和Hashtable实现类 271 7.6.2 SortedMap接口和TreeMap实现类 276 7.6.3 WeakHashMap实现类 279 7.6.4 IdentityHashMap实现类 280 7.6.5 EnumMap实现类 281 7.7 HashSet和HashMap的性能选项 282 ...

    Java 基础核心总结 +经典算法大全.rar

    BIO 和 NIO 拷贝文件的区别操作系统的零拷贝 选择器(Selectors) 选择键(SelectionKey) 示例:简易的客户端服务器通信 集合 集合框架总览 -、Iterator Iterable ListIterator 二、Map 和 Collection 接口Map 集合体系...

Global site tag (gtag.js) - Google Analytics