如何自己手写一个散列表

108 阅读4分钟

什么是散列表

散列表(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]: 

问题

机智的你觉得上面代码有哪些可以优化的点呢?

扩展阅读

点击查看更多