前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手写HashMap基础部分代码

手写HashMap基础部分代码

作者头像
捞月亮的小北
发布2024-03-04 09:02:41
680
发布2024-03-04 09:02:41
举报
文章被收录于专栏:捞月亮的小北捞月亮的小北

代码展示:

代码语言:javascript
复制
package com.north.hashmap;

import java.util.Map;

/**
 * @Author North
 * @Date 2024/3/3
 * 手写HashMap
 */
@SuppressWarnings("all")
public class MyHashMap<K , V> {
    /**
     * 哈希表
     */
    private Node<K,V>[] table;

    /**
     * 键值对的个数
     */
    private int size;

    @SuppressWarnings("unchecked")
    public MyHashMap() {
        // 注意:new数组的时候,不能使用泛型。这样写是错误的:new Node<K,V>[16];
        this.table = new Node[16];
    }

    static class Node<K,V>{
        /**
         * key的hashCode()方法的返回值。
         * 哈希值,哈希码
         */
        int hash;
        /**
         * key
         */
        K key;
        /**
         * value
         */
        V value;
        /**
         * 下一个结点的内存地址
         */
        Node<K,V> next;

        /**
         * 构造一个结点对象
         * @param hash 哈希值
         * @param key 键
         * @param value 值
         * @param 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;
        }

        @Override
        public String toString(){
            return "["+key+", "+value+"]";
        }
    }

    /**
     * 获取集合中的键值对的个数
     * @return 个数
     */
    public int size(){
        return size;
    }

    /**
     * 向MyHashMap集合中添加一个键值对。
     * @param key 键
     * @param value 值
     * @return value,如果key重复,则返回oldValue,如果key不重复,则返回newValue
     */
    public V put(K key, V value){
        /*
        【第一步】:处理key为null的情况
            如果添加键值对的key就是null,则将该键值对存储到table数组索引为0的位置。
        【第二步】:获得key对象的哈希值
            如果添加键值对的key不是null,则就调用key的hashcode()方法,获得key的哈希值。
        【第三步】:获得键值对的存储位置
            因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,
            那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。
        【第四步】:将键值对添加到table数组中
            当table[i]返回结果为null时,则键键值对封装为Node对象并存入到table[i]的位置。
            当table[i]返回结果不为null时,则意味着table[i]存储的是单链表。我们首先遍历单链表,如果遍历出来节点的
            key和添加键值对的key相同,那么就执行覆盖操作;如果遍历出来节点的key和添加键值对的key都不同,则就将键键
            值对封装为Node对象并插入到单链表末尾。
         */
        if(key == null){
            return putForNullKey(value);
        }
        // 程序执行到此处说明key不是null
        // 获取哈希值
        int hash = key.hashCode();
        // 将哈希值转换成数组的下标
        int index = Math.abs(hash % table.length);
        // 取出下标index位置的Node
        Node<K,V> node = table[index];
        if(null == node){
            table[index] = new Node<>(hash, key, value, null);
            size++;
            return value;
        }
        // 有单向链表(遍历单向链表,尾插法)
        Node<K,V> prev = null;
        while(null != node){
            if(node.key.equals(key)){
                V oldValue = node.value;
                node.value = value;
                return oldValue;
            }
            prev = node;
            node = node.next;
        }
        prev.next = new Node<>(hash, key, value, null);
        size++;
        return value;
    }

    private V putForNullKey(V value) {
        Node<K,V> node = table[0];
        if(node == null){
            table[0] = new Node<>(0, null,value,null);
            size++;
            return value;
        }
        // 程序可以执行到此处,说明下标为0的位置上有单向链表
        Node<K, V> prev = null;
        while(node != null){
            if(node.key == null){
                V oldValue = node.value;
                node.value = value;
                return oldValue;
            }
            prev = node;
            node = node.next;
        }
        prev.next = new Node<>(0, null,value,null);
        size++;
        return value;
    }

    /**
     * 通过key获取value
     * @param key 键
     * @return 值
     */
    public V get(K key){
        /*
        【第一步】:处理key为null的情况
        如果查询的key就是null,则就在table数组索引为0的位置去查询。
        【第二步】:获得key对象的哈希值
        如果查询的key不是null,则就调用key的hashcode()方法,获得key的哈希值。
        【第三步】:获得键值对的存储位置
        因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,
        那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。
        【第四步】:遍历单链表,根据key获得value值
        如果table[i]返回的结果为null,则证明单链表不存在,那么返回null即可
        如果table[i]返回的结果不为null时,则证明单链表存在,那么就遍历整个单链表。如果遍历出来节点的key和查询
        的key相同,那么就返回遍历出来节点的value值;如果整个单链表遍历完毕,则遍历出来节点的key和查询的key都不
        相等,那么就证明查询key在链表中不存在,则直接返回null即可。
         */
        if(null == key){
            Node<K,V> node = table[0];
            if(null == node){
                return null;
            }
            // 程序执行到这里,数组下标为0的位置不是null。就是有单向链表。
            while(node != null){
                if(null == node.key){
                    return node.value;
                }
                node = node.next;
            }
        }
        // key不是null
        int hash = key.hashCode();
        int index = Math.abs(hash % table.length);
        Node<K,V> node = table[index];
        if(null == node){
            return null;
        }
        while(null != node){
            if(node.key.equals(key)){
                return node.value;
            }
            node = node.next;
        }
        return null;
    }

    /**
     * 重写toString方法,直接输出Map集合是会调用。
     * @return ""
     */
    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < table.length; i++) {
            Node<K,V> node = table[i];
            // 如果node不是空,就遍历整个单向链表
            while(node != null){
                sb.append(node);
                node = node.next;
            }
        }
        return sb.toString();
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档