虽然很想很早就想写一个hash表,但一直都未去实现。通过这次机会,算是对hash表有了一个比较直观的了解,主要有以下几点(都是个人见解):
1.哈希表的目的在于加快查找速度,用一个形象的比喻就是hash是将一个排好序的数据存入 数组中,所以在查找时能通过这个索引迅速找到所需要的元素,在hash表中,数组才是主体,链表只是辅助,甚至可以不存在。
2.产生这个索引(在hash中是key)的函数和方法各种各样,而判别这个方法优劣就是让其尽可能少得产生冲突,因为产生冲突后,就会调用处理冲突的方法,无论哪一种方法都不是很合适,以链表为例,显然链表是不适合查找的,所以这个方法对表性能的影响是很大的。这里我才用了和系统差不多的方法,采用了&运算,其实类似于mod,但速度更快。
3.处理冲突的方法也有不少。其中我比较明白的是再散列法和拉链法。在散列法的思想很简单,就是产生冲突后重新计算hash函数地址(索引值)直到找到空的空间放数据。而拉链法则是运用的链表的逻辑连续特性,使不同的数据存在同一个哈希地址下。我采用了第二种,所以对其优缺点比较了解,缺点很明显就是增加查找的复杂度,而相对与再散列法的优点还是很大的,可以避免重复的计算hash地址。
4.如何resize,当装载数与数组长度的比例大于等于比例因子,这是说明数组容量快要饱和,为了避免产生大量的冲突,必须采用resize。
而在写代码的过程中也考虑了很久。比如在数组中存什么,数组长度定义多少之类。这里我说说我的方法。数组中我存的是链表的首节点,因为链表的特性,只要知道头就可以获得整个链表,于此匹配的,我也采用了头插法,当产生冲突,我把它放在链表的头位置。这样代码可以减少不少。至于数组长度我定义为2^n,原因与&运算有关,因为2^n-1的二进制所有位都为1,这样的与运算可以使冲突尽量减少。当产生冲突时,数组长度扩大一倍,感觉也是比较合理的。
以下是部分代码
/**
* 结点类
* @author zrq
*所有数据类都必须继承这个结点类
*/
public class Node {
private String account;//账号是唯一标示
private String password;
private Node Next;
public int hashcode()
{
int hash=1;
hash=Integer.parseInt(account);
return hash;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Node getNext() {
return Next;
}
public void setNext(Node next) {
Next = next;
}
}
由于我是采用了数字作为测试数据,所以节点中的属性是直接定义的(account是唯一的)。重写了hashcode(),产生的方法就是采用了account这个唯一的属性(不通用)。
public class MyHashMap {
private int initial_size = 16;// 数组初始大小
private int finally_size = 16;// 用来记录现在数组大小
private double balance = 0.75;// 装载因子
private Node[] mapNodes;//组成数组
private int count = 0;// 用于计数
int hash;
int index;
// 初始化时开辟数组空间
public MyHashMap() {
mapNodes = new Node[initial_size];
}
public void put(Node node) {
hash = node.hashCode();// 可修改
index = hash & (finally_size - 1);// 获得索引位置
Node fi = mapNodes[index];
if (fi == null) {
mapNodes[index] = node;
count++;
if (count > finally_size * balance)
reSize();
} else {
// System.out.println("test " + mapNodes[index].head.getAccount());
node.setNext(fi);
mapNodes[index] = node;
}
}
/**
* 通过account查找的方法
* @param x:account
* @return: password
*/
public String GetValue(String x) {
Node s = new Node();
s.setAccount(x);
boolean f = false;
int hash = s.hashcode();
index = hash & (finally_size - 1);
Node n = mapNodes[index];
while (n != null) {
if (n.getAccount().equals(x)) {
f = true;
break;
}
n = n.getNext();
}
if (f)
return n.getPassword();
else {
System.out.println(finally_size);
return "null";
}
}
/**
* 当达到装载因子时扩容
*/
public void reSize() {
count=0;
Node[] copy = new Node[finally_size * 2];
for (int i = 0; i < finally_size; i++) {
Node node = mapNodes[i];
Node dnext;
while (node != null) {
//System.out.println("de"+node.getAccount());
dnext = node.getNext();
int index = node.hashcode() & (finally_size * 2 - 1);
if (copy[index] == null) {
node.setNext(null);
copy[index] = node;
count++;
} else {
Node t = copy[index];
node.setNext(t);
copy[index] = node;
}
node = dnext;
}
}
finally_size = finally_size * 2;
mapNodes = copy;
}
}
写的比较急,没怎么优化。
以下是测试类。我用1000000个连续的号码进行存储和查找的测试。系统的时间大概在2600ms,我的大概在3000ms。查找100000个数据,我的约为90ms,系统在120ms左右,当然这和我的node设置的针对性也有关系。
以下是myhashmap的测试主函数(将da放的位置改变就可以测试不同的时间)
public static void main(String[] args) {
// TODO Auto-generated method stub
File fi = new File("F:\\test.txt");
try {
MyHashMap map = new MyHashMap();
// HashMap< String, String> map=new HashMap<String, String>();
Scanner scan = new Scanner(fi);
while (scan.hasNext()) {
String s = scan.next();
Node node = new Node();
node.setAccount(s);
node.setPassword(s);
map.put(node);
}
int x=100000;
Date da = new Date();
long n = da.getTime();
while(x-->0)
{
map.GetValue(""+x);
}
Date de = new Date();
long t = de.getTime();
System.out.println(t - n + "ms");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
以下是系统的测试主函数
public static void main(String[] args) {
// TODO Auto-generated method stub
File fi = new File("F:\\test.txt");
try {
HashMap<String, String> map = new HashMap<String, String>();
Scanner scan = new Scanner(fi);
int x = 100000;
while (scan.hasNext()) {
String s = scan.next();
map.put(s, s);
}
Date da = new Date();
long n = da.getTime();
while (x-- > 0) {
map.get("" + x);
}
Date de = new Date();
long t = de.getTime();
System.out.println(t - n + "ms");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
hash的内容还有很多,先写到这。至于哈希算法,很多在安全领域涉及,比如MD5(深奥啊),以后再补充吧。
分享到:
相关推荐
Java语言使用hashmap实现向购物车添加删除修改商品,显示商品信息
在Java编程中,HashMap是一个非常重要的数据结构,它实现了Map接口,提供了键值对的存储功能,具有快速存取和高效查找的特点。HashMap基于哈希表(也称为散列表)原理,通过键对象的哈希码来定位元素,进而实现O(1)...
在Java中,HashMap是一种广泛使用的数据结构,它基于哈希表的Map接口实现。哈希表是一种通过哈希过程将键映射到特定位置的数据结构,该位置存储了键对应的值。在详细探讨Java中HashMap的工作机制之前,首先需要理解...
Java中的HashMap是一种基于散列机制的Map接口的实现,它允许我们存储键值对。键是唯一的,而值可以重复。HashMap在处理数据时非常高效,因为其操作的时间复杂度接近于O(1)。这是通过使用散列函数将键映射到相应的...
JavaScript中的HashMap并不是内置的数据结构,但在许多开发场景中,我们需要实现类似Java中HashMap的功能,用于存储键值对数据。在JavaScript中,我们通常使用对象(Object)来模拟HashMap的行为,因为对象的属性名...
本资源详细介绍了 Java 中的 HashMap 类,包括其实现机制、Hash 存储机制、集合存储机制等方面的知识点。 1. HashMap 和 HashSet 的关系 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,虽然...
`HashMap`是一种基于哈希表实现的`Map`接口,提供了一个非同步的、允许使用`null`键和值的存储结构。`HashMap`通过计算对象的哈希码来存储和检索对象,这使得数据可以在常数时间内被访问。 - **特点**: - **非...
用js代码实现java中hashmap 的所有功能
5. Java中HashMap的应用和实现 详细解释: 1. 哈希函数的原理和应用 哈希函数是一种将输入数据转换为固定长度的哈希码的函数。在HashMap中,哈希函数用于将Key转换为一个哈希码,然后根据哈希码将Key-Value对存储...
"基于HashMap的用户标签处理兼Java中HashMap实现原理研究" 本文研究了基于HashMap的用户标签处理方法,并对Java中HashMap的实现原理进行了深入研究。HashMap是一种高效的数据结构,可以快速地存储和检索数据。本文...
总的来说,理解并熟练使用`HashMap`对于Java开发者来说至关重要,因为它在数据存储和检索方面提供了高效且灵活的解决方案。在学习和使用`HashMap`时,不仅要掌握其基本用法,还要了解其内部工作原理,包括哈希函数、...
总结来说,Java 8 HashMap键与Comparable接口的结合使用,使得我们能够在保持HashMap高效性能的同时,方便地对键进行排序,特别是在使用Java 8的新特性如Stream API时。通过实现Comparable接口,我们能够自定义对象...
Java中的HashMap是一个非常重要的数据结构,它实现了Map接口,提供了键值对的高效存储和访问。HashMap基于哈希表(也称为散列表)原理工作,它允许用户通过键(Key)快速查找对应的值(Value)。在HashMap中,键和值...
Java使用HashMap实现并查集 Java使用HashMap实现并查集是指使用Java语言中的HashMap数据结构来实现并查集。并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以...
在本资源中,我们将学习如何使用 Java 语言实现一个简单的购物车系统,其中使用 HashMap 来存放用户想买的商品信息。下面是该资源中的知识点总结: ConnDB.java ConnDB.java 是一个用于连接数据库的类,该类中...
Java 中 HashMap 类的使用详解 HashMap 是 Java 语言中最常用的集合类之一,它实现了 Map 接口,提供了 put、get、keySet 等常用方法来存储和检索数据。本文将详细介绍 HashMap 类的使用,包括其常用方法、特点和...
通过以上步骤,我们成功地实现了使用Java WebService读取`HashMap`中的数值的功能。这种做法不仅适用于简单的数据交换场景,还能够扩展到更复杂的业务逻辑处理中。对于开发者而言,理解和掌握这一技术是构建稳定可靠...
HashMap是Java编程语言中一种非常重要的数据结构,它实现了Map接口,主要用于存储键值对。HashMap内部基于哈希表(Hash Table)实现,利用哈希函数将键映射到一个特定的位置,以便快速查找和插入元素。以下是HashMap...
在Java编程语言中,集合框架是开发者日常工作中不可或缺的一部分,HashMap作为其中的重要成员,它的实现原理对于理解Java性能优化和数据结构有深远的意义。HashMap是一个基于哈希表的数据结构,它实现了Map接口,...
Java8之后新增挺多新东西,接下来通过本文给大家介绍Java8 HashMap的实现原理分析,对java8 hashmap实现原理相关知识感兴趣的朋友一起学习吧