最近的有些东西要用到这玩意,于是又重新来复习了。。。。
以下代码按照我自己对于hash表的印象打出来的,代码不多,中间有些小遗忘才翻了翻别人的代码。
底层代码可能需要看看(毕竟我想知道怎么做到超大数据查询优化效果的hh),位运算和泛型都在底层代码里体现了,可我不太熟悉哈哈。。另外不知道有没有迭代器来简化搜索的过程。
一:链地址法:
package hashtable;
import java.util.*;
//拉链法哈希存储,求出一个hash值,后来如果有相同hash值的,在这个拉链下面找是否有相同的值,如果没有相同的值就存储进去。不然就不进行变动。
public class Hash {
public static int MAX = 5;
List[] list;
public Hash() {
list = new LinkedList[MAX];
for(int i = 0;i < MAX;i++) {
list[i] = new LinkedList<Integer>();
}
}
public void add(int key) {
int h = hash(key);
if(!contains(key)) {
list[h].add(key);
}
}
public int detete(int key) {
int h = hash(key);
int temp = -1;
//直接remove指定下标好像不行,会和直接删除数据混合。。。。
Iterator<Integer> it = list[h].iterator();
while(it.hasNext()) {
Integer cur = it.next();
if(key==cur) {
list[h].remove(cur);
return cur;
}
}
return temp;
}
public boolean contains(int key) {
int h = hash(key);
Iterator<Integer> iterator = list[h].iterator();
while(iterator.hasNext()) {
Integer elem = iterator.next();//它刚开始是指向最开始的值的地址的前一个地方。
if(elem==key) {
return true;
}
}
return false;
}
public int hash(int key) {
return key%MAX;
}
public void show() {
for(int i = 0;i < MAX;i++) {
if(list[i].isEmpty()) {
System.out.println("--empty--");
}else {
System.out.print(list[i].get(0));
for(int j = 1;j < list[i].size();j++) {
System.out.print("->"+list[i].get(j));
}
System.out.println();
}
}
}
public static void main(String[] args) {
Hash hash = new Hash();
System.out.println("请输入数据:");
Scanner input = new Scanner(System.in);
for(int i = 0;i < 12;i++) {
hash.add(input.nextInt());
}
System.out.println(hash.contains(12));
hash.show();
System.out.println("输入删除数据:");
hash.detete(input.nextInt());
hash.show();
}
}
二:开放寻址法
这里一般会有一个增长因子的概念,也叫作负载因子,简单点说就是已经被占的位置与总位置的一个百分比,比如一共十个位置,现在已经占了七个位置,就触发了扩容机制,因为它的增长因子是0.7,也就是达到了总位置的百分之七十就需要扩容。拿HashMap来说,当它当前的容量占总容量的百分之七十五的时候就需要扩容了。而且这个扩容也不是简单的把数组扩大,而是新创建一个数组是原来的2倍,然后把原数组的所有元素都重新Hash一遍放到新的数组。
我这里就直接粗暴地将旧数组里面的内容拷贝到新数组就完事了,要做到正确的话应该是要将原先的数组全部hash到新数组当中。
package hashtable;
import java.util.Arrays;
public class Hash2<V> {
//就不删除节点了,直接抹掉原来的value值就行。
public Node<V>[] arr;
//默认长度16
int DEFAULT_LENGTH = 1<<2;
//拓增因子。
double INCREASED_RATE = 0.75;
//当前hash表长度
int Maxlen;
//当前有效元素总数
static int numElem = 0;
public Hash2() {
Maxlen = DEFAULT_LENGTH;
arr = new Node[Maxlen];
for(int i = 0;i < Maxlen;i++) {
arr[i] = new Node();
}
}
//添加时如果key相同就进行冲突处理
/**
* 添加过程中如果出现重复键值,则覆盖原来的数据。
* 寻找key不存在的地方添加
* hash表中的nkey决定是否存在,我们自己的key决定是否相同。
* hash表中的nkey处如果有内容了,就会依次往后推移。如果中途遇到了相同的key,就会覆盖掉。
* 如果没遇到相同的key就会最后找到一个空地方停下来。
*
* @param key 键(唯一)
* @param value 值
* @return void
*
*/
public void add(int key,V value) {//因为如果删除了,就会有null存在,所以从一开始就直接全为空
rehash();
int nkey = hash(key);
Node temp = new Node(key,value);
if(contains(key)) {
while(arr[nkey].key!=key) {
nkey = (nkey+1)%Maxlen;
}
arr[nkey] = temp;
return;
}
while(arr[nkey].value!=null) {//hash表直接定位,找不到再往后推移,推移的时hash表的nkey,
nkey = (nkey+1)%Maxlen; //key值作为唯一判别,如果重复就覆盖掉原来的节点返回
// System.out.println("12793y9823");
}
arr[nkey] = temp;
numElem++;
}
/**
* remove在这里根本是依赖于判断key值是否存在然后删除
* @param key
* @return V(取决于用户决定的类型)
*/
public V remove(int key) {
if(!contains(key)) {
return null;
}
int nkey = hash(key);
V res = null;
while(arr[nkey].key!=key) {
nkey = (nkey+1)%Maxlen;
}
res = arr[nkey].value;
arr[nkey].value = null;
numElem--;
return res;
}
public boolean contains(int key) {//key值不相同才能加,然后如果转换过来的hash值相同就向后推移。
int nkey = hash(key);
int temp = nkey;
if(arr[nkey].value==null) {//为空
return false;
}
while((temp+1)%Maxlen!=nkey) {
if(arr[temp].key==key) {
return true;
}
temp = (temp+1)%Maxlen;
// System.out.println("fhfduisf");
}
return false;
}
public V get(int key) {//获取hash表中元素。
if(!contains(key)) {
return null;
}
int nkey = hash(key);
while(arr[nkey].key!=key) {
nkey = (nkey+1)%Maxlen;
}
return arr[nkey].value;
}
public int hash(int key) {
return key%5;
}
public void show() {
System.out.println("展示结果如下:");
System.out.println("--------------------------------");
for(int i = 0;i < Maxlen;i++) {
if(arr[i].value!=null) {
System.out.println(arr[i].key+": "+arr[i].value);
}
}
System.out.println("--------------------------------");
}
/**
* 当hash表内存不足时,自动进行扩容。扩容为原来的二倍大小。
*/
public void rehash() {
if(INCREASED_RATE*Maxlen <= numElem) {
System.out.println("数组已满,进行扩容,当前数组最大长度为:"+Maxlen);
int temp = Maxlen;
Maxlen = Maxlen<<1;
Node<V>[] narr = new Node[Maxlen];
for(int i = 0;i < Maxlen;i++) {
narr[i] = new Node<V>();
}
for(int i = 0;i < temp;i++) {
narr[i] = arr[i];
}
System.out.println("扩容后数组最大长度为:"+Maxlen);
arr = narr;
}
}
public static void main(String[] args) {
Hash2<Character> hash = new Hash2();
// for(int i = 0;i < hash.Maxlen;i++) {
// System.out.print(hash.arr[i].value+" ");
// }
hash.add(2, 'c');
hash.add(5, 'd');
hash.add(7, 'f');
hash.add(8, 'c');
hash.add(9, 'l');
hash.add(10, 'r');
hash.add(11, 'y');
hash.add(12, 'k');
System.out.println("有效元素个数:"+numElem);
hash.show();
System.out.println(hash.get(8));
}
}