什么是散列表
散列表(HashMap,又称哈希表)其实是一种基于数组的扩展,它利用了数组的随机访问特性,通过散列函数对key求值并除以数组长度取余,该余数则是key映射到数组的下标,在该下标的位置存放key对应的value。
如何设计散列表?
散列函数,
要能够快速并均匀地把key映射成数组下标。
装载因子
load factor
装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。
冲突解决
开放寻址法
- 线性探测法
- 二次探测法
- 双重散列法
链表法
顾名思义了
实现一个散列表
实现一个基于链表法解决冲突问题的散列表
对照上面的设计原则,但这里先不处理装载因子和动态扩容了。
代码实现
public class YHashMap {
private float loadFactor = 1;
private Node[] arr;
private int len;
/** 记录现在保有多少个元素 */
private int count;
public YHashMap(int len) {
this.len = len;
arr = new Node[this.len];
}
public void put(String key, Object val) {
if (key == null || val == null) {
return;
}
int hash = hash(key);
int index = hash % len;
if (null == arr[index]) {
Node newNode = new Node(key, val);
arr[index] = newNode;
count++;
} else {
Node node = new Node("", null);
node.setNext(arr[index]);
do {
node = node.next;
if (node.key.equals(key)) {
//如果已经存在Key相同的元素了,则覆盖之前的Val,并返回
node.val = val;
return;
}
} while(node.next != null);
// 执行到这里,说明上面没有找到key相同的元素,则新加一条到链表尾部
Node newNode = new Node(key, val);
node.next = newNode;
count++;
}
}
/**
* 根据key来查询val
* @param key
* @return
*/
public Object get(String key) {
if (null == key) {
return null;
}
int hashVal = hash(key);
int index = hashVal % len;
Node node = arr[index];
if (null == node) {
return null;
} else {
do {
if (node.key.equals(key)) {
// 找到key相同的元素,则返回结果
return node.val;
}
node = node.next;
} while (node != null);
}
// 未找到结果
return null;
}
/**
* 根据key删除节点
* @param key
* @return
*/
public boolean remove(String key) {
if (null == key) {
return false;
}
int hashVal = hash(key);
int index = hashVal % len;
Node node = arr[index];
Node pre = new Node(null, null);
pre.setNext(node);
while(node != null) {
if (node.key.equals(key)) {
if (node == arr[index]) {
// 注意第一个节点要特殊处理
arr[index] = node.next;
}
pre.setNext(node.getNext());
count--;
return true;
}
pre = node;
node = node.next;
}
return false;
}
/**
* Hash 函数,返回值≥0
* @param key
* @return
*/
private int hash(String key) {
if (null == key) {
return 0;
}
int len = key.length();
int val = 0;
for (int i = 0; i < len; i++) {
val += key.charAt(i);
}
return val;
}
/**
* 打印哈希表中的所有值
*/
public void printAll() {
System.out.println("当前元素数量:" + count);
for (int i = 0; i < arr.length; i++) {
Node node = arr[i];
System.out.print("[" + i + "]: ");
while(null != node) {
System.out.print("(" + node.key + ", " + node.val + "), ");
node = node.next;
}
System.out.println();
}
System.out.println();
}
public static class Node {
private String key;
private Object val;
private Node next;
public Node(String key, Object val) {
this.key = key;
this.val = val;
}
public void setNext(Node next) {
this.next = next;
}
public Node getNext() {
return next;
}
}
}
测试代码
public class YHashMapTest {
private static YHashMap hashMap;
public static void main(String[] args) {
hashMap = new YHashMap(10);
// 存键值对测试
hashMap.put("12", "12-");
hashMap.put("13", "13-");
hashMap.put("14", "14-");
hashMap.put("22", "22-");
hashMap.put("10", "10-");
hashMap.put("21", "21-");
hashMap.put("ab", "ab-");
hashMap.put("ba", "ba-");
hashMap.put("31", "31-");
hashMap.put("41", "41-");
hashMap.printAll();
// 覆盖测试
hashMap.put("ab", "阿巴阿巴-");
hashMap.put("31", "三姨-");
hashMap.printAll();
//取值测试
testGet("");
testGet("xx");
testGet("10");
testGet("14");
testGet("31");
hashMap.printAll();
//删除测试
testRemove("");
testRemove("xx");
hashMap.printAll();
testRemove("10");
testRemove("ba");
testRemove("ba");
hashMap.printAll();
testRemove("ab");
testRemove("14");
testRemove("41");
hashMap.printAll();
testRemove("22");
testRemove("13");
testRemove("12");
testRemove("31");
testRemove("21");
testRemove("21");
hashMap.printAll();
}
private static void testRemove(String key) {
boolean isRv = hashMap.remove(key);
LogHelper.v("remove key(" + key + "): " + isRv );
}
private static void testGet(String key) {
Object val = hashMap.get(key);
LogHelper.v("getKey(" + key+ "): " + val);
}
}
测试结果
当前元素数量:10
[0]: (13, 13-), (22, 22-), (31, 31-),
[1]: (14, 14-), (41, 41-),
[2]:
[3]:
[4]:
[5]: (ab, ab-), (ba, ba-),
[6]:
[7]: (10, 10-),
[8]:
[9]: (12, 12-), (21, 21-),
当前元素数量:10
[0]: (13, 13-), (22, 22-), (31, 三姨-),
[1]: (14, 14-), (41, 41-),
[2]:
[3]:
[4]:
[5]: (ab, 阿巴阿巴-), (ba, ba-),
[6]:
[7]: (10, 10-),
[8]:
[9]: (12, 12-), (21, 21-),
getKey(): null
getKey(xx): null
getKey(10): 10-
getKey(14): 14-
getKey(31): 三姨-
当前元素数量:10
[0]: (13, 13-), (22, 22-), (31, 三姨-),
[1]: (14, 14-), (41, 41-),
[2]:
[3]:
[4]:
[5]: (ab, 阿巴阿巴-), (ba, ba-),
[6]:
[7]: (10, 10-),
[8]:
[9]: (12, 12-), (21, 21-),
remove key(): false
remove key(xx): false
当前元素数量:10
[0]: (13, 13-), (22, 22-), (31, 三姨-),
[1]: (14, 14-), (41, 41-),
[2]:
[3]:
[4]:
[5]: (ab, 阿巴阿巴-), (ba, ba-),
[6]:
[7]: (10, 10-),
[8]:
[9]: (12, 12-), (21, 21-),
remove key(10): true
remove key(ba): true
remove key(ba): false
当前元素数量:8
[0]: (13, 13-), (22, 22-), (31, 三姨-),
[1]: (14, 14-), (41, 41-),
[2]:
[3]:
[4]:
[5]: (ab, 阿巴阿巴-),
[6]:
[7]:
[8]:
[9]: (12, 12-), (21, 21-),
remove key(ab): true
remove key(14): true
remove key(41): true
当前元素数量:5
[0]: (13, 13-), (22, 22-), (31, 三姨-),
[1]:
[2]:
[3]:
[4]:
[5]:
[6]:
[7]:
[8]:
[9]: (12, 12-), (21, 21-),
remove key(22): true
remove key(13): true
remove key(12): true
remove key(31): true
remove key(21): true
remove key(21): false
当前元素数量:0
[0]:
[1]:
[2]:
[3]:
[4]:
[5]:
[6]:
[7]:
[8]:
[9]:
问题
机智的你觉得上面代码有哪些可以优化的点呢?