[Java] 手写一个简单的HashMap

[Java] 手写一个简单的HashMap

1 前言

阅读此篇博客,你需要学习:

  • HashMap原理以及版本的更替
  • Object的hashCode原理
  • 哈希冲突的解决方法
  • Java泛型

若内容存在错误,欢迎指正。

2 知识

记忆技巧:

  • 本质上,HashMap就是一个Node<K, V>数组,Node内部的next实现链表。
  • 多个操作存在重复的操作。
    • putVal(K key, V value, Node<K, V>[] table)方法:将key-value放入指定的Node数组中。put()方法和rehash时都需要调用,不要写死在put()函数中。
    • getIndex(K key, int length)方法:获取key的index。get()和put()方法都会使用。
  • put时,不要只专注于添加Node,一定需要注意key.hashCode()相同的情况,即覆盖value。
  • key的对比要使用hashCode。
  • 注意链表的遍历写法。

编写策略:

  • 散列函数:hashCode() + 除留求余法
  • 冲突解决:链地址法
  • 链式插入策略:头插法
  • 检查策略:先检查后放入
  • 扩容策略:节点重新hash

3 代码

public class MyHashMap<K, V> {

    private static final class Node<K, V> {
        K key;
        V value;
        Node<K, V> next;

        public Node(K key, V value, Node<K, V> next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

    private int size;
    private int capacity;
    private Node<K, V>[] table;

    private final static int DEFAULT_CAPACITY = 16;
    private final static float LOAD_FACTOR = 0.75f;

    public MyHashMap() {
        capacity = DEFAULT_CAPACITY;
        table = new Node[capacity];
        size = 0;
    }

    public MyHashMap(int capacity) {
        this.capacity = capacity;
        table = new Node[capacity];
        size = 0;
    }

    public void put(K key, V value) {
        if (size >= capacity * LOAD_FACTOR) rehash();
        putVal(key, value, table);
    }

    private void putVal(K key, V value, Node<K, V>[] table) {
        int hashCode = key.hashCode();
        int index = getIndex(hashCode, capacity);
        if (table[index] == null) {
            table[index] = new Node<>(key, value);
        } else {
            Node<K, V> node = table[index];
            while (node != null) {
                if (node.key.hashCode() == hashCode) {
                    node.value = value;
                    return;
                }
                node = node.next;
            }
            Node<K, V> newNode = new Node<>(key, value, table[index]);
            table[index] = newNode;
        }
        size++;
    }

    private int getIndex(int hashCode, int length) {
        return hashCode % length;
    }

    public V get(K key) {
        int hashCode = key.hashCode();
        int index = getIndex(hashCode, capacity);
        if (table[index] != null) {
            Node<K, V> node = table[index];
            while (node != null) {
                if (node.key.hashCode() == hashCode) return node.value;
                node = node.next;
            }
        }
        return null;
    }

    private void rehash() {
        size = 0;
        capacity *= 2;
        Node<K, V>[] newTable = new Node[capacity];
        for (Node<K, V> node : table) {
            while (node != null) {
                Node<K, V> next = node.next;
                node.next = null;
                putVal(node.key, node.value, newTable);
                node = next;
            }
        }
        table = newTable;
    }

    public int getSize() {
        return size;
    }
}

4 测试

在MyHashMap类中编写简单的函数,用于获取HashMap内部信息:

public void showContext() {
    System.out.println("{size=" + size + ", capacity=" + capacity + "}");
    System.out.println("-------------------");
    for (Node<K, V> node : table) {
        if (node == null) System.out.println("[null]");
        else {
            while (node != null) {
                System.out.print("[" + node.value + "]");
                if (node.next != null) System.out.print("-->");
                node = node.next;
            }
            System.out.println();
        }
    }
    System.out.println("-------------------");
}

测试方向:

  • 链地址法是否正常
  • 扩容是否正常
  • get是否正常
public static void main(String[] args) {
    MyHashMap<Integer, Integer> hashMap = new MyHashMap<>(2);
    System.out.println("Notice: Init hashMap");
    hashMap.showContext();
    hashMap.put(1, 1);
    hashMap.put(3, 3);
    System.out.println("Notice: Has been put 1=1 3=3");
    hashMap.showContext();

    hashMap.put(2, 2);
    hashMap.put(4, 4);
    System.out.println("Notice: Has been put 1=1 3=3, 2=2, 4=4");
    hashMap.showContext();
    System.out.println("Notice: get(3): " + hashMap.get(3));
    System.out.println("Notice: get(5): " + hashMap.get(5));
}
Notice: Init hashMap
{size=0, capacity=2}
-------------------
[null]
[null]
-------------------
Notice: Has been put 1=1 3=3
{size=2, capacity=2}
-------------------
[null]
[3]-->[1]
-------------------
Notice: Has been put 1=1 3=3, 2=2, 4=4
{size=4, capacity=8}
-------------------
[null]
[1]
[2]
[3]
[4]
[null]
[null]
[null]
-------------------
Notice: get(3): 3
Notice: get(5): null
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单HashMap实现,仅供参考: ```java public class MyHashMap<K, V> { private static final int DEFAULT_CAPACITY = 16; // 默认容量为16 private static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认负载因子为0.75 private int size; // 元素个数 private int threshold; // 阈值,当size >= threshold时,需要进行扩容 private float loadFactor; // 负载因子 private Node<K, V>[] table; // 存储元素的数组 public MyHashMap() { this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); } public MyHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } public MyHashMap(int initialCapacity, float loadFactor) { if (initialCapacity <= 0) { throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); } if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new IllegalArgumentException("Illegal load factor: " + loadFactor); } this.loadFactor = loadFactor; this.table = new Node[initialCapacity]; this.threshold = (int) (initialCapacity * loadFactor); } public V put(K key, V value) { if (key == null) { return putForNullKey(value); } int hash = hash(key.hashCode()); int index = indexFor(hash, table.length); for (Node<K, V> node = table[index]; node != null; node = node.next) { if (node.hash == hash && (key == node.key || key.equals(node.key))) { V oldValue = node.value; node.value = value; return oldValue; } } addEntry(hash, key, value, index); return null; } private V putForNullKey(V value) { for (Node<K, V> node = table[0]; node != null; node = node.next) { if (node.key == null) { V oldValue = node.value; node.value = value; return oldValue; } } addEntry(0, null, value, 0); return null; } private void addEntry(int hash, K key, V value, int index) { Node<K, V> node = table[index]; table[index] = new Node(hash, key, value, node); if (++size >= threshold) { resize(); } } private void resize() { int oldCapacity = table.length; int newCapacity = oldCapacity * 2; Node<K, V>[] newTable = new Node[newCapacity]; for (Node<K, V> node : table) { while (node != null) { int index = indexFor(node.hash, newCapacity); Node<K, V> next = node.next; node.next = newTable[index]; newTable[index] = node; node = next; } } table = newTable; threshold = (int) (newCapacity * loadFactor); } public V get(K key) { if (key == null) { return getForNullKey(); } int hash = hash(key.hashCode()); int index = indexFor(hash, table.length); for (Node<K, V> node = table[index]; node != null; node = node.next) { if (node.hash == hash && (key == node.key || key.equals(node.key))) { return node.value; } } return null; } private V getForNullKey() { for (Node<K, V> node = table[0]; node != null; node = node.next) { if (node.key == null) { return node.value; } } return null; } public boolean containsKey(K key) { return get(key) != null; } public boolean isEmpty() { return size == 0; } public int size() { return size; } private int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } private int indexFor(int hash, int length) { return hash & (length - 1); } private static class Node<K, V> { private final int hash; private final K key; private V value; private Node<K, V> next; public Node(int hash, K key, V value, Node<K, V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } } } ``` 以上是一个简单HashMap实现,其中采用了链表法解决哈希冲突,并且在元素个数达到阈值时进行了扩容。需要注意的是,这个实现并不完整,仅作为参考。在实际使用中,还需要考虑并发性、迭代器等因素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值