Hash算法小谈
一、初识Hash:
一直都只知道Hash是一种算法,但是具体是什么一直都是模糊的概念。之前知道Object类中有hashCode()方法,此方法返回该对象的哈希码值。而哈希码值对象的内存地址。Hash其实就是一种存储数据的结构,一般来说我们所了解熟
悉的结构有:数组、链表、队列、Map、树、图等。但最基本的两种结构是数组和链表,可以由数组和链表组合成不同的新的结构,至于是“数组+数组”、“链表+链表”还是“数组+链表”就要看你的需求是什么,想要达到什么样的存储效果。
二、Hash是什么?
现在我理解的Hash就是一种存储数据的结构,这种结构是“数组+链表”形式的一种结构。
三、为什么要使用Hash?
这种结构既有数组的优点又有链表相对应的一些优点,对大量数据的存储很有优势,增删改查相对而言都比较方便。Hash中的算法可以使自己设置的,也可以使用系统中提供的。一般来说都是根据自己的需求来设置自己所需要的存储数据的结构,即:设置自己所需要的Hash结构。
四、Hash的建立:
首先我们建立一个模型:一个长度为n的数组,每一个数组元素存的是一段链表的头节点,链表长度为m(每个链表的m值相互无关),而链表的每个节点存储的则是一个Add类的对象,这样无形中就形成了一个二维的数据存储结构,我们称之为数组加链表模型。假如有0~99这100个数需要存到这种模型中,我们可以让数组的第一个元素专门存储第一位为0的数(即一位数),链表长度为10,存储的数据分别为0、1、2、3、4、5、6、7、8、9;第二个元素存储第一位为1的数……以此类推,直到全部存进。现在我们要查找65,假设数组名为a,我们只需要找到a[6],然后再循环查找链表的下一个节点,直到找到65,只需要5次。而用链表查找,则需要查找65次。当然如果用数组定义的话直接a[65]就能得到,但是如果需要在65与66之间再插入一个值就比较麻烦了,而上述数组加链表模型依然能轻松的解决这一问题。
Hash中有“阀值”这个概念,至于“阀值”是什么都有不同的理解,一般的理解是:所有元素的个数与散列表位置的比值。对于“阀值”值的设定也是由自己的需求设定的,系统提供的“阀值”大小是:0.75,一般来说想要达到结构空间上的效率就尽量将“阀值”设置得大些,想要达到时间上的效率就将“阀值”设置得小些,一般都是将阀值得达到空间与时间的均衡。当链表节点过多,也就是超过阀值时,就需要扩充数组,即需要再散列,也就是reHash。再散列后各个数据的存储位置都发生了改变,散列结构和原来的是完全不一样的。自己设置一种简单的Hash算法(规则)用来对数据进行存储,个人认为此法是用来得到元素存储在数组的那一个位置的值。
/**
* Hash算法
* @param value
* @return
*/
int Hash(int value){
int sum=0;
while(value/10!=0){
sum+=Math.pow(16,value%10);
value=value/10;
}
return sum%prime;
}
/**
*
* @param element:数组的第几号元素
* @param adder:要添加的元素
* @param array:添加至的数组
* @return:array
*/
Add[] addto(int element,Add adder,Add[] Array){
//若超过数组的大小,则抛出异常
if(element>Array.length){
throw new RuntimeException("地址超出数组长度,添加失败");
}
//若数组为空
else if(Array[element]==null){
Array[element]=adder;
}
else{
Add adder1=Array[element];
//链表结点数
int count=0;
//若得到的链表节点上的元素不为空
if(adder.getNextadder()!=null){
count++;
//添加下一个结点
adder1=adder.getNextadder();
}
adder.setNextadder(adder);
adder.setLastadder(adder1);
if (count > Array.length * 0.75) {
// 链表节点过多,扩展数组
System.out.println("链表节点过多,扩展数组");
Array = reHash(Array);
}
}
return Array;
}
/**
* 当数组需要扩张时的重新散列方法
*
* @param Array
*/
Add[] reHash(Add[] Array) {
//重新散列得到的数组
Add[] reArray = new Add[(int) (Array.length *2)];
//将此数组添加到队列中
List<Add> list = new ArrayList<Add>();
prime = Prime(reArray.length);
for (int i = 0; i < Array.length; i++) {
for (Add adder = Array[i]; adder != null; adder = adder.getNextadder()) {
list.add(adder);
try {
// 把原本的上下链关系全部断开
adder.getLastadder().setNextadder(null);
adder.setLastadder(null);
} catch (NullPointerException npe) {
System.out.println("空指针");
}
}
}
for (int i = 0; i < list.size(); i++) {
// 逐个添加
adder = list.get(i);
element = Hash(adder.getValue1());
addto(element, adder, reArray);
}
Array = reArray;
return Array;
}
五、Hash各个算法的实现(这里仅实现了增和查):
/**
* 添加元素的方法
* @param DataArray
*/
public void add(Add[] DataArray){
Scanner sc=new Scanner(System.in);
System.out.println("请输入需要添加的个数,由系统随机产生");
int in=sc.nextInt();
//创建添加元素的线程对象,并且启动
Mythread th=new Mythread();
th.start();
h:for(int i=0;i<in;i++){
value=random();
element=Hash(value);
Add checkadder=DataArray[element];
while(checkadder!=null){
if(checkadder.getValue1()==value){
continue h;
}
checkadder=checkadder.getNextadder();
}
adder=new Add(value);
DataArray=addto(element,adder,DataArray);
}
th.setFlag(false);
try{
Thread.sleep(5000);
}catch(Exception ef){
ef.printStackTrace();
}
search(DataArray);
}
/**
* 查找元素的方法
* @param DataArray
*/
public void search(Add[] DataArray){
int sum=0;
int length=DataArray.length;
System.out.println("共有"+length+"个元素");
for(int i=0;i<length;i++){
int num=1;
adder=DataArray[i];
if(adder!=null){
while(adder.getNextadder()!=null){
num++;
adder=adder.getNextadder();
}
}
else{
num=0;
}
sum+=num;
System.out.println("第"+i+"个元素的链表中共有"+num+"个对象");
}
System.out.println("共有"+sum+"个对象");
}