了解哈希表的实现
秦九韶算法
秦九韶算法是中国南宋时期的数学家秦九韶提出的一种多项式简化算法。在西方被称作霍纳算法。
拉链法
为了解决hash表中的下标冲突
实现原理
当hash值冲突时,在冲突hash值下标中创建一个集合存放数据
//判断是否找到key
AtomicBoolean isKey = new AtomicBoolean(false);
// 当前下标有数据
List list = (List) storage.get(hashIndex.intValue());
list.forEach((i)->{
List li = (List)i;
String s = li.get(0).toString();
if (s==key){
li.set(1,value);
isKey.set(true);
}
});
代码实现
计算hash值
使用秦九韶算法计算出hash值, 质数常量*总和+unicode 返回时求余当前长度,获取数组下标
/**
* @Description: 使用拉链法实现hash表
* @param: str 传入的key
* @param: size 当前存放哈希表数组长度
* @return java.math.BigInteger
* @author xiaoCao
* @date 2022/8/2 15:55
**/
public static BigInteger getHashIndex(String str,long size){
long sum=0;
for (int i=0;i<str.length();i++) {
//1.获取该字符的unicode值
String hash = Integer.toHexString(str.charAt(i));
int unicode = Integer.parseInt(hash, 16);
// 2.使用秦久韶算法 a0+x(a1+x(a2+x(a3+.......an-2+x(an-1+an*x)))) OFFSET代表an sum代表x an-1代表unicode
sum = OFFSET*sum+unicode;
}
BigInteger a = new BigInteger(String.valueOf(sum));
return a.remainder(new BigInteger(Long.toString(size)));
}
存放数据
存放数据时判断当前hash表是否已满,满了就返回false,后面会说怎么自动扩容
/**
* @Description: 使用拉链法解决下标冲突
* @param: key
* @param: value
* @return boolean
* @author xiaoCao
* @date 2022/8/2 15:57
**/
public static boolean put(String key,Object value) throws NewException {
//判断当前hash表是否已满
if (count>=length){
return false;
}
BigInteger hashIndex = getHashIndex(key, length);
List arr = (List) storage.get(hashIndex.intValue());
//当前下标无数据
if (arr.get(0)==""){
storage.add(hashIndex.intValue(),new ArrayList(){{
add(Arrays.asList(key,value));
}});
}else{
//判断是否找到key
AtomicBoolean isKey = new AtomicBoolean(false);
// 当前下标有数据
List list = (List) storage.get(hashIndex.intValue());
list.forEach((i)->{
List li = (List)i;
String s = li.get(0).toString();
if (s==key){
li.set(1,value);
isKey.set(true);
}
});
if (!isKey.get()){
list.add(Arrays.asList(key,value));
}
storage.set(hashIndex.intValue(),list);
}
count++;
return true;
}
获取数据
获取当前传入key的hash值下标,判断当前数据集合当前下标是否有值,没有就返回自定义异常类
/**
* @Description: 获取hash表中的数据
* @param: key
* @return java.lang.String
* @author xiaoCao
* @date 2022/8/2 16:07
**/
public static String get(String key) throw NewException{
BigInteger hashIndex = getHashIndex(key,length);
List list =(List) storage.get(hashIndex.intValue());
if(list.size()==0){
throw new NewException("Without this key");
}
AtomicReference<String> value = new AtomicReference<>("");
list.forEach((i)->{
List li = (List)i;
String s = li.get(0).toString();
if (s==key){
value.set(li.get(1).toString());
}
});
return value.toString();
}
手动扩容
这边使用的时arrayList集合扩容就比较方便,我选择直接循环添加需要扩容的大小,如果传入的扩容大小比当前集合size小的话,直接使用自定义异常类,返回异常原因
/**
* @Description:当数据已经超出定义的数据大小 需要扩容调用的方法
* @param: newLength
* @return boolean
* @author xiaoCao
* @date 2022/8/2 16:10
**/
public static boolean resize(int newLength) throws NewException {
if (newLength<length){
throw new NewException("index error");
}
for (int i =length ; i<newLength;i++){
//定义为String类型
storage.add(Arrays.asList("",listClass));
}
length = newLength;
return true;
}
删除数据
传入一个key,获取这个key值的下标,判断数据集合当前下标是否有值,没有返回异常告知异常原因,有值遍历当前下标的数据是否可以找到对应的key值,找不到返回异常告知异常原因
/**
* @Description:删除数据
* @param: key
* @return boolean
* @author xiaoCao
* @date 2022/8/2 17:44
**/
public static boolean del(String key){
if (count ==0){
try {
throw new NewException("Index Out of Bounds");
} catch (NewException e) {
e.printStackTrace();
}
}
BigInteger hashIndex = getHashIndex(key, length);
List list = (List)storage.get(hashIndex.intValue());
if (list.size()==0){
try {
throw new NewException("del error not date");
} catch (NewException e) {
e.printStackTrace();
}
}
for (int i =0; i<list.size();i++){
List lis = (List)list.get(i);
if (lis.get(0) == key){
list.set(i,Arrays.asList("",""));
}
}
return true;
}
}
自动扩容
在put的时候判断当前数据是否超过自定义的扩容上限值,超出就扩容两倍
//自动扩容2倍
if (count > Math.round(length*0.8)){
int i = toPrime(length * 2);
resize(i);
}
自定义异常类
/**
* @Description:自定义异常类
* @param: null
* @return * @return null
* @author xiaoCao
* @date 2022/8/2 17:28
**/
class NewException extends Exception{
public NewException() {
super();
}
public NewException(String str) {
super(str);
}
}
总结
只要求出hash值,其他的都很简单;
解决hash值下标冲突方法还有开放地址法;
开放地址法下面还有,在hash化,线性探查,二次探测,需要的小伙伴可以去详细了解一下;
上面代码不太明白的小伙伴可以私信我