手写hashmap,实现put和get

本文介绍了HashMap的基础原理,包括JDK1.7前后的结构变化,旨在减少哈希碰撞并提升get操作速度。文中通过源码解析详细展示了自定义HashMap的put和get方法实现,涉及哈希函数、链表结构以及冲突解决策略。此外,还提供了完整的MyHashMap类实现,包括resize()扩容方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

众说周知,在jdk1.7之前HashMap的底层结构是由数组+链表构成的,而在jdk1.8之后HashMap的底层结构变为数组+链表+红黑树,目的是为了解决单链表查询的深度问题,提高get(key)的速度。本文基于数组+链表,简单实现hashmap的put、get等方法。 

Hash()

根据同一散列函数计算出的散列值如果不同,那么输入值肯定也不同。但是,根据同一散列函数计算出的散列值如果相同,输入值不一定相同。

两个不同的输入值,根据同一散列函数计算出的散列值相同的现象叫做碰撞。

hash()是减少hash碰撞、进行高效存取的关键。hashmap是可以存储 null的key和value的,null的值就存储在数组[0]上,进行异或操作都是让数组下标的位置更加随机和分布均匀,减少冲突。

 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

Put()

HashMap 中, hashCode() 方法决定了对象会被放到哪个 bucket 里,当多个 对象的哈希值冲突时,equals() 方法决定了这些对象是否是 同一个对象
当我们使用put(key,value) 方法添加数据时 , 首先会对 key 进行 hash 计算,再进行
hash % DEFAULT_INITIAL_CAPACITY得到在数组中的存放位置;
------- 如果该位置上为空 , 那就新建一个 node 对象 , 保存 key- value到该数组位置上 ;
------- 如果计算出来的数组位置上有元素的话 ; 就沿着此数组位置对应的单链表上的结点一个个比较
        ------如果遇到相同的key,就用新的 value 替 换掉旧的value
        ------如果找不到相同的 key ,就新建一个 node 结点,保存 hash 值,key value ,然后插入到此单链表的尾部。
 public V put(K k, V v) {

        Node<K, V> node;

        int index, hash;

        hash = hash(k);
        index = hash % DEFAULT_INITIAL_CAPACITY;
        node = new Node<>(hash, k, v, null);
//        首次put时创建数组
        if (talbe == null || talbe.length == 0) {
            this.talbe = new Node[DEFAULT_INITIAL_CAPACITY];
        }
//        考虑扩容
        if (size > talbe.length * DEFAULT_LOAD_FACTOR) {
            resize();
        }
//        数组该位置为空, 头部插入
        if (talbe[index] == null) {
            talbe[index] = node;
            size++;
            return v;
        } else {
            Node<K, V> head = talbe[index];
            Node<K, V> tail = null;
            while (head != null) {
//                如果hash值相同,就再根据equals判断
                if (hash(head.key) == hash(k) && k.equals(head.key)) {
                    head.value = v;
                    return v;
                }
                tail = head;
                head = head.next;
            }
            tail.next = node;
            size++;
            return v;
        }
    }

 Get()

get()也类似,本质上都是先计算key的hash值,根据hash值计算出数组的存放位置,再通过equals()判断是否为同一个key,是则返回。

public V get(K key) {
        int hash = hash(key);
        int index = hash % DEFAULT_INITIAL_CAPACITY;
        Node<K, V> first, e;

        first = talbe[index];
        if (first == null) {
            return null; //头节点为空,直接返回null
        } else if (first.hash == hash && first.key == key || (key != null && key.equals(first.key))) {
            return first.value;//头节点就是,直接返回
        } else if ((e = first.next) != null) {
            while ((e = e.next) != null) {//顺着单链表查找
                if (e.hash == hash && e.key == key || (key != null && key.equals(e.key))) {
                    return e.value;
                }
            }
        }
        return null;
    }

完整代码

接口类MyMap

public interface MyMap<K,V> {

    int size();

    boolean isEmpty();

    V get(K key);

    V put(K k,V v);

    V remove(K k);

    V update(K k,V v);


    interface Entry<K,V>{

        K getKey();

        V getValue();
    }
}

实现类MyHashMap

import java.util.*;

public class MyHashMap<K, V> implements MyMap<K, V> {

    //    默认容量: 16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    //    默认加载因子: 0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //    数组
    Node[] talbe;

    int size;


    public MyHashMap() {
        this.size = 0;
    }


    //定义Node节点内部类,存储key-value
    static class Node<K, V> implements MyMap.Entry<K, V> {

        int hash;
        K key;
        V value;
        Node<K, V> next;

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

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }
    }


    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public V get(K key) {
        int hash = hash(key);
        int index = hash % DEFAULT_INITIAL_CAPACITY;
        Node<K, V> first, e;

        first = talbe[index];
        if (first == null) {
            return null; //头节点为空,直接返回null
        } else if (first.hash == hash && first.key == key || (key != null && key.equals(first.key))) {
            return first.value;//头节点就是,直接返回
        } else if ((e = first.next) != null) {
            while ((e = e.next) != null) {//顺着单链表查找
                if (e.hash == hash && e.key == key || (key != null && key.equals(e.key))) {
                    return e.value;
                }
            }
        }
        return null;
    }

    @Override
    public V put(K k, V v) {

        Node<K, V> node;

        int index, hash;

        hash = hash(k);
        index = hash % DEFAULT_INITIAL_CAPACITY;
        node = new Node<>(hash, k, v, null);
//        首次put时创建数组
        if (talbe == null || talbe.length == 0) {
            this.talbe = new Node[DEFAULT_INITIAL_CAPACITY];
        }
//        考虑扩容
        if (size > talbe.length * DEFAULT_LOAD_FACTOR) {
            resize();
        }
//        数组该位置为空, 头部插入
        if (talbe[index] == null) {
            talbe[index] = node;
            size++;
            return v;
        } else {
            Node<K, V> head = talbe[index];
            Node<K, V> tail = null;
            while (head != null) {
//                如果hash值相同,就再根据equals判断
                if (hash(head.key) == hash(k) && k.equals(head.key)) {
                    head.value = v;
                    return v;
                }
                tail = head;
                head = head.next;
            }
            tail.next = node;
            size++;
            return v;
        }
    }

    @Override
    public V remove(K key) {
        int hash = hash(key);
        int index = hash % DEFAULT_INITIAL_CAPACITY;
        Node<K, V> nodes = talbe[index];
        if (nodes != null) {
            if (nodes.getKey().equals(key)) {
                talbe[index] = nodes.next;
                size--;
            } else {
                while (!nodes.next.getKey().equals(key)) {
                    nodes = nodes.next;
                }
                V value = nodes.next.getValue();
                nodes.next = nodes.next.next;
                size--;
                return value;
            }
        }
        return null;
    }

    @Override
    public V update(K k, V v) {
        return put(k, v);
    }


    public Set<K> Keyset() {
        Set<K> kSet = new HashSet<>();
        for (Node nodes : talbe) {
            if (nodes != null) {
                while (nodes != null) {
                    kSet.add((K) nodes.getKey());
                    nodes = nodes.next;
                }
            } else {
                continue;
            }
        }
        return kSet;
    }

    public Collection<V> values() {
        ArrayList<V> values = new ArrayList<>();
        for (Node nodes : talbe) {
            if (nodes != null) {
                while (nodes != null) {
                    values.add((V) nodes.getValue());
                    nodes = nodes.next;
                }
            } else {
                continue;
            }
        }
        return values;
    }


    public void print() {
        for (Node node : talbe) {
            Node nodes = node;
            if (nodes != null) {
                while (nodes != null) {
                    K key = (K) nodes.getKey();
                    V value = (V) nodes.getValue();
                    System.out.print(key + " : " + value + "\t");
                    nodes = nodes.next;
                }
                System.out.println();
            }
        }
    }


    private void resize() {
        int newCapacity = talbe.length << 2;
        Node<K, V>[] newTable = new Node[newCapacity];
        newTable = Arrays.copyOf(talbe, talbe.length);
        this.talbe = newTable;
    }


    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值