HashMap 数组+链表实现

手撕HashMap主要是为了能更好的理解HashMap的数据结构原理。只实现了 put、get、remove。

JDK 实现的实在太复杂。这个实现是实现最简单的版本。后续如果有时间会逐一补上 自动扩容,数组+红黑树的实现。

前提条件

  1. 数组+链表有基本了解

实现逻辑

package com.company;

public class MaLinHashMap {

    private int arrayLength;
    private Node[] bucketArray;

    public MaLinHashMap() {
        this.arrayLength = 16;
        this.bucketArray = new Node[arrayLength];
    }

    public MaLinHashMap(int arrayLength) {
        this.arrayLength = arrayLength;
        this.bucketArray = new Node[arrayLength];
    }

    private class Node {
        private Object key;
        private Object value;
        private Node next;

        public Node(Object key, Object value, Node next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    public void put(Object key, Object value) {
        int index = getBucketArrayIndex(key);
        Node headNode = bucketArray[index];
        Node newNode = new Node(key, value, null);
        if (headNode == null) {
            bucketArray[index] = newNode;
        } else {
            while (true) {
                if (headNode.next == null) {
                    headNode.next = newNode;
                    break;
                }
                headNode = headNode.next;
            }
        }
    }

    public void remove(Object key) {
        int index = getBucketArrayIndex(key);
        Node head = bucketArray[index];
        Node prev = null;
        while (head != null) {
            if (head.key.equals(key))
                break;
            prev = head;
            head = head.next;
        }
        if (head == null) {
            return;
        }
        if (prev != null) {
            prev.next = head.next;
        } else {
            bucketArray[index] = head.next;
        }
    }

    public Object get(Object key) {
        int index = getBucketArrayIndex(key);
        Node headNode = bucketArray[index];
        while (headNode != null) {
            if (headNode.key.equals(key)) return headNode.value;
            headNode = headNode.next;
        }
        return null;
    }

    /**
     * 获取数组下标
     *
     * @param key 键
     * @return 下标
     */
    private int getBucketArrayIndex(Object key) {
        // 这里不采用 & 与运算符,直接使用hash%arrayLength可能出现负数非常尴尬。
        // return key.hashCode() % arrayLength;
        // TODO & 使用后可以确保得到一个 0~15 整数,但是位运算符实战看不懂。这个还是抄袭的HashMap获取index
        // if ((p = tab[i = (n - 1) & hash]) == null)
        int hash = (hash = key.hashCode()) ^ (hash >>> 16);
        return arrayLength - 1 & hash;
    }
}

测试逻辑

package com.company;

public class MaLinHashMapTest {
    public static void main(String[] args) {
        int count = 100000;
        test(32768, count);
        test(16384, count);
        test(8192, count);
        test(4096, count);
        test(2048, count);
        test(1024, count);
        test(512, count);
        test(256, count);
        test(128, count);
        test(64, count);
        test(32, count);
        test(16, count);
    }

    public static void test(int arrayLength, int count) {
        long start = System.currentTimeMillis();
        MaLinHashMap map = new MaLinHashMap(arrayLength);
        for (int i = 0; i < count; i++) {
            map.put("testKey" + i, i);
        }
        long timeConsuming = System.currentTimeMillis()-start;
        String output = String.format("数组长度%s, 数据量:%s, 耗时(毫秒): %s", arrayLength, count, timeConsuming);
        System.out.println(output);
    }
}

测试结果

"C:Program FilesJavajdk1.8.0_211injava.exe" -Xms2048m -Xmx2048m "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA Community Edition 2020.1.1libidea_rt.jar=56358:C:Program FilesJetBrainsIntelliJ IDEA Community Edition 2020.1.1in" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_211jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_211jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_211jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_211jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_211jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_211jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_211jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_211jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_211jrelibext
ashorn.jar;C:Program FilesJavajdk1.8.0_211jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_211jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_211jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_211jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_211jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_211jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_211jrelibjce.jar;C:Program FilesJavajdk1.8.0_211jrelibjfr.jar;C:Program FilesJavajdk1.8.0_211jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_211jrelibjsse.jar;C:Program FilesJavajdk1.8.0_211jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_211jrelibplugin.jar;C:Program FilesJavajdk1.8.0_211jrelib
esources.jar;C:Program FilesJavajdk1.8.0_211jrelib
t.jar;F:workspacelinked_learnoutproductionlinked_learn" com.company.MaLinHashMapTest
数组长度32768, 数据量:10000, 耗时(毫秒): 4
数组长度16384, 数据量:10000, 耗时(毫秒): 2
数组长度8192, 数据量:10000, 耗时(毫秒): 1
数组长度4096, 数据量:10000, 耗时(毫秒): 2
数组长度2048, 数据量:10000, 耗时(毫秒): 2
数组长度1024, 数据量:10000, 耗时(毫秒): 1
数组长度512, 数据量:10000, 耗时(毫秒): 2
数组长度256, 数据量:10000, 耗时(毫秒): 3
数组长度128, 数据量:10000, 耗时(毫秒): 6
数组长度64, 数据量:10000, 耗时(毫秒): 10
数组长度32, 数据量:10000, 耗时(毫秒): 18
数组长度16, 数据量:10000, 耗时(毫秒): 25

Process finished with exit code 0
原文地址:https://www.cnblogs.com/linma/p/13144060.html