Java 手写设计HashMap源码,让面试官膜拜

news2024/11/30 2:46:26

Java 手写HashMap源码,让面试官膜拜

一,手写源码

这是一个模仿HashMap的put,get功能的自定义的MyHashMap

package cn.wxs.demo;

import java.io.Serializable;
import java.util.*;
import java.util.function.BiConsumer;

class MyHashMap<K, V> extends AbstractMap<K,V>
        implements Map<K,V>, Cloneable, Serializable {
    private static final int DEFAULT_CAPACITY = 16;
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;

    private Node<K, V>[] table; // 存储桶数组
    private transient int size; // 键值对数量

   private transient int modCount;  //总数

    private transient int threshold; // 扩容阈值
    private float loadFactor; // 负载因子


    @Override
    public void forEach(BiConsumer<? super K, ? super V> action) {
        MyHashMap.Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (MyHashMap.Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e.key, e.value);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    public MyHashMap() {
        this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    public MyHashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity <= 0)
            throw new IllegalArgumentException("Invalid initial capacity");
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Invalid load factor");

        this.table = new Node[initialCapacity];
        this.size = 0;
        this.loadFactor = loadFactor;
        this.threshold = (int) (initialCapacity * loadFactor);
    }

    public V get(Object key) {
        Node<K, V> node = getNode((K) key);
        return (node != null) ? node.value : null;
    }

    public V put(K key, V value) {
        if (key == null)
            putForNullKey(value);
        else
            putNonNullKey(key, value);
        return value;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return null;
    }


    private void putNonNullKey(K key, V value) {
        int hash = hash(key);
        int index = indexFor(hash, table.length);

        // 遍历链表,查找是否已存在相同的键
        for (Node<K, V> node = table[index]; node != null; node = node.next) {
            if (node.hash == hash && Objects.equals(node.key, key)) {
                // 如果找到相同的键,更新对应的值
                node.value = value;
                return;
            }
        }

        // 如果没有找到相同的键,将新的键值对添加到链表头部
        addNode(hash, key, value, index);
    }

    private void putForNullKey(V value) {
        // 对于 null 键,存储在数组的第一个位置
        for (Node<K, V> node = table[0]; node != null; node = node.next) {
            if (node.key == null) {
                // 如果找到 null 键,更新对应的值
                node.value = value;
                return;
            }
        }

        // 如果没有找到 null 键,将新的键值对添加到链表头部
        addNode(0, null, value, 0);
    }

    private void addNode(int hash, K key, V value, int bucketIndex) {
        // 检查是否需要扩容
        if (size >= threshold)
            resize(2 * table.length);

        // 将新的节点添加到链表头部
        Node<K, V> newNode = new Node<>(hash, key, value, table[bucketIndex]);
        table[bucketIndex] = newNode;
        size++;
        modCount++;
    }

    private Node<K, V> getNode(K key) {
        if (key == null)
            return getNodeForNullKey();

        int hash = hash(key);
        int index = indexFor(hash, table.length);

        // 遍历链表,查找指定的键
        for (Node<K, V> node = table[index]; node != null; node = node.next) {
            if (node.hash == hash && Objects.equals(node.key, key))
                return node;
        }

        return null;
    }

    private Node<K, V> getNodeForNullKey() {
        // 对于 null 键,存储在数组的第一个位置
        for (Node<K, V> node = table[0]; node != null; node = node.next) {
            if (node.key == null)
                return node;
        }
        return null;
    }

    private int hash(K key) {
        // 计算键的哈希值
        return (key == null) ? 0 : key.hashCode();
    }

    private int indexFor(int hash, int length) {
        // 根据哈希值和数组长度计算索引位置
        return hash & (length - 1);
    }

    private void resize(int newCapacity) {
        Node<K, V>[] oldTable = table;
        int oldCapacity = oldTable.length;

        // 检查是否达到容量上限
        if (oldCapacity >= 1 << 30)
            threshold = Integer.MAX_VALUE;
        else
            threshold = (int) (newCapacity * loadFactor);

        // 创建新的数组,将旧的键值对重新分配到新的数组中
        Node<K, V>[] newTable = new Node[newCapacity];
        transfer(oldTable, newTable);
        table = newTable;
    }

    private void transfer(Node<K, V>[] src, Node<K, V>[] dest) {
        // 遍历旧的数组,将键值对重新分配到新的数组中
        for (Node<K, V> node : src) {
            while (node != null) {
                Node<K, V> next = node.next;
                int index = indexFor(node.hash, dest.length);
                node.next = dest[index];
                dest[index] = node;
                node = next;
            }
        }
    }

    static class Node<K, V> {
        final int hash; // 哈希值
        final K key; // 键
        V value; // 值
        Node<K, V> next; // 下一个节点

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

    public static void main(String[] args) {
        MyHashMap<String,String> param = new MyHashMap<>();
        param.put("你好","你好");
        param.put("你好1","你好1");
        param.put("你好2","你好2");
        param.put("你好3","你好3");
        param.put("你好4","你好4");
        param.put("你好5","你好4");
        param.put("你好6","你好4");
        param.put("你好7","你好4");
        param.put("你好8","你好4");
        param.put("你好9","你好4");
        param.put("你好10","你好4");
        param.put("你好11","你好4");
        param.put("你好12","你好4");
        param.put("你好13","你好4");
        param.put("你好14","你好4");
        param.put("你好15","你好4");
        param.put("你好16","你好4");
        param.put("你好17","你好4");
        param.put("你好18","你好4");
        param.put("你好20","你好4");
        param.put("你好21","你好4");
        param.put("你好22","你好4");
        param.put("你好23","你好4");
        param.put("你好24","你好4");
        param.put("你好25","你好4");
        param.put("你好26","你好4");
        param.put("你好27","你好4");
        param.put("你好28","你好4");
        param.put("你好29","你好4");
        param.put("你好30","你好4");
        param.put("你好31","你好4");
        param.put("你好32","你好4");

        param.forEach((k,v)-> System.out.println(k));
//        System.out.println(param.get("你好4"));
    }
}

在这里插入图片描述

二,代码介绍

1.这是一个简单的自定义哈希映射(HashMap)的实现。功能不太完善,代码不太优雅,细节不够好,没有源码的那么好。后面我会慢慢进行优化
2.这段代码实现了一个简化版本的 HashMap,包含了 put 和 get 方法,并使用了数组+链表的存储结构。注意,这里的红黑树部分并未实现,只使用了链表来处理冲突。由于短时间没有办法能按自己的方式写出来全部的功能和底层设计,难度还是比较大的后期我会慢慢的把存储结构改成数组+链表+红黑树的数据结构

请注意,这只是一个简化版本的实现,并没有处理扩容、红黑树转换等复杂的细节。实际的 HashMap 实现要复杂得多。如果你对完整的实现感兴趣,建议你查阅 Java 的 HashMap 源代码,以便更好地理解和学习。

三,下期完善功能

size()
remove(key)
containsKey(key)
containsValue(value)

三,HashMap

HashMap 是 Java 中的一个常用的数据结构,它实现了 Map 接口,并且基于哈希表实现。HashMap 允许存储键值对,并且提供了快速的插入、查找和删除操作。

下面是 HashMap 的一些重要特点和概念:

  • 哈希表HashMap 内部使用了一个数组来存储数据,这个数组被称为哈希表。哈希表的每个元素称为一个桶(bucket),每个桶可以存储一个或多个键值对。通过计算键的哈希值,可以确定键值对在哈希表中的位置。

  • 哈希函数:哈希函数用于将键映射到哈希表中的索引位置。好的哈希函数能够将键均匀地分布在哈希表的不同位置上,以减少冲突的概率。在 HashMap 中,键的 hashCode() 方法被用作哈希函数。

  • 冲突:当两个不同的键通过哈希函数计算得到的索引位置相同时,就发生了冲突。HashMap 使用链表或红黑树来解决冲突。当冲突较少时,使用链表;当链表长度超过一定阈值时,转换为红黑树,以提高查找的效率。

  • 键的唯一性:在 HashMap 中,键是唯一的。如果尝试将一个已经存在的键插入到 HashMap 中,它的值将被更新为新的值。

  • null 键HashMap 允许存储一个键为 null 的键值对。这个键将被存储在哈希表的第一个位置上。

  • 迭代顺序HashMap 不保证键值对的迭代顺序,它通常是不确定的。如果需要有序的键值对集合,可以使用 LinkedHashMap

下面是一些常用的操作方法:

  • put(key, value):向 HashMap 中插入一个键值对。
  • get(key):根据键获取对应的值。
  • remove(key):根据键删除对应的键值对。
  • containsKey(key):检查是否包含指定的键。
  • containsValue(value):检查是否包含指定的值。
  • size():返回 HashMap 中键值对的数量。

需要注意的是,HashMap 是非线程安全的,如果在多线程环境中使用,需要进行适当的同步处理,或者使用线程安全的 ConcurrentHashMap

HashMap 的时间复杂度通常是常数级别的,即 O(1),但在最坏的情况下,可能会达到 O(n),其中 n 是 HashMap 中存储的键值对数量。

希望这个简介对你理解 HashMap 有所帮助!如果你有任何其他问题,请随时提问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1299763.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【解密考研英语:Python数据分析与可视化】

解密考研英语&#xff1a;Python数据分析与可视化 背景数据集技术选型功能实现创新点 大家好&#xff0c;欢迎阅读我的CSDN博客&#xff01;今天我将分享一项有关考研英语真题的数据分析与可视化项目&#xff0c;希望对考研学子提供更有针对性的复习帮助。 背景 作为考研学子…

【TwinCAT学习笔记 1】TwinCAT开发环境搭建

写在前面 作为技术开发人员&#xff0c;开启任何一项开发工作之前&#xff0c;首先都要搭建好开发环境&#xff0c;所谓磨刀不误砍材工&#xff0c;一定要有耐心&#xff0c;一次不行卸载再装。我曾遇到过一个学生&#xff0c;仅搭建环境就用了两周&#xff0c;这个过程也是一…

Docker容器的可视化管理工具—DockerUI本地部署与远程访问

文章目录 前言1. 安装部署DockerUI2. 安装cpolar内网穿透3. 配置DockerUI公网访问地址4. 公网远程访问DockerUI5. 固定DockerUI公网地址 前言 DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基…

3接上篇 我的自定义GPTs的改进优化 与物理世界连接成功 GPTs的创建与使用定义和执行特定任务的功能模块 通过API与外部系统或服务的交互

https://blog.csdn.net/chenhao0568/article/details/134875067?spm1001.2014.3001.5502 从服务器日志里看到请求多了一个“location” 23.102.140.123 - - [08/Dec/2023:14:02:20 0800] "GET /getWeather.php?location&locationNewYork HTTP/1.1" 200 337 &…

公式识别任务各个链条全部打通

目录 引言公式识别任务是什么&#xff1f;公式识别任务解决方案初探使用建议写在最后 引言 随着LaTeX-OCR模型转换问题的解决&#xff0c;公式识别任务中各个链条已经全部打通。小伙伴们可以放开膀子干了。 解决业界问题的方案&#xff0c;并不是单独训练一个模型就完事了&am…

Spring Bean基础

写在最前面: 本文运行的示例在我github项目中的spring-bean模块&#xff0c;源码位置: spring-bean 前言 为什么要先掌握 Spring Bean 的基础知识&#xff1f; 我们知道 Spring 框架提供的一个最重要也是最核心的能力就是管理 Bean 实例。以下是其原因&#xff1a; 核心组件…

data_loader返回的每个batch的数据大小是怎么计算得到的?

data_loader是一个通用的术语&#xff0c;用于表示数据加载器或数据批次生成器。它是在机器学习和深度学习中常用的一个概念。 一、data loader 数据加载器&#xff08;data loader&#xff09;是一个用于加载和处理数据集的工具&#xff0c;它可以将数据集划分为小批次&#…

Oracle 中换行chr(10)、回车chr(13)

一、前言 chr(n)&#xff1a;返回 ascii 值对应的字符。 ascii(char)&#xff1a;返回字符 char对应的ascii 值。 chr(n) 和 ascii(char) 作用刚好是相反的。 SQL> select chr(65) from dual; 控制台显示&#xff1a;ASQL> select ascii(A) from dual; 控制台显示&am…

Oracle高可用一家老小全在这里

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

FL Studio2024永久免费体验版下载

FL Studio中文绿色21版是一款无需要安装的汉化版本&#xff0c;它是一款非常专业的音频编辑软件&#xff0c;可以让你的音乐突破想象力的限制哦&#xff0c;FL Studio21中文版可以制作出不同音律的节奏&#xff0c;FL Studio内置众多电子合成音色&#xff0c;只Styrus可以让人激…

鸿蒙开发组件之ForEach列表

一、ForEach函数 ForEach函数是一个迭代函数&#xff0c;需要传递两个必须参数和一个可选参数。主要通过迭代来获取参数arr中的数据不断的生成单个Item来生成鸿蒙中的列表样式 二、先创建单个的Item的UI 通过嵌套Row与Column来实现单个Item的UI。例如图中没有折扣的可以看成一…

C++-引用和指针区别

文章目录 1.变量的组成2.指针2.1 定义2.2 使用指针操作变量2.3 为什么使用指针 3.引用3.1 定义3.2 引用注意事项 4.引用和指针的区别 1.变量的组成 变量的组成&#xff1a;变量地址&#xff0c;变量名&#xff0c;变量值 例&#xff1a; int i 12;2.指针 2.1 定义 指针用于存…

【Python】简单的翻译软件

用translate包和tkinter写一个简单的桌面翻译软件。 1、窗口设置&引入包&#xff1a; from tkinter import * from tkinter.ttk import * from tkinter.messagebox import * import translatewinTk() win.title(翻译) win.geometry("600x400")win.mainloop() …

【Linux系统编程】初步运用git工具

介绍&#xff1a; 使用git之前首先要先认识gitee/github&#xff0c;gitee/github是一个远程仓库网站。git是平台专门开发的一个操控工具&#xff0c;是一个开源的分布式版本控制系统&#xff0c;我们使用git工具来与gitee/github来取得联系。 git的推送使用&#xff1a; git既…

解决electron修改主进程后需要重启才生效

nodemon 是一种工具&#xff0c;可在检测到目录中的文件更改时通过自动重新启动节点应用程序来帮助开发基于 node.js 的应用程序 nodemon 特性 自动重新启动应用程序。检测要监视的默认文件扩展名。默认支持 node&#xff0c;但易于运行任何可执行文件&#xff0c;如 python、…

uniapp获取wifi连接状态

当使用Uniapp开发移动应用时&#xff0c;我们经常需要获取设备的连接状态&#xff0c;特别是WiFi连接状态。下面是一个简短的关于在Uniapp中获取WiFi连接状态的博客&#xff1a; 在Uniapp中&#xff0c;要获取设备的WiFi连接状态&#xff0c;我们可以利用uni.getNetworkType接…

【HarmonyOS开发】详解常见容器的使用

声明式UI提供了以下8种常见布局&#xff0c;开发者可根据实际应用场景选择合适的布局进行页面开发。 布局 应用场景 线性布局&#xff08;Row、Column&#xff09; 如果布局内子元素超过1个&#xff0c;且能够以某种方式线性排列时优先考虑此布局。 层叠布局&#xff08;St…

2023-12-05 Qt学习总结8

点击 <C 语言编程核心突破> 快速C语言入门 Qt学习总结 前言二十三 QSqlDatabase数据库二十四 cmake工程管理文件总结 前言 要解决问题: 学习qt最核心知识, 多一个都不学. 二十三 QSqlDatabase数据库 QSqlDatabase 是 Qt 框架中关于数据库的统一封装&#xff0c;它支持…

【Hexo】自动在中英文之间添加空格

本文首发于 ❄️慕雪的寒舍 如题&#xff0c;中英文添加空格是比较规范的写法&#xff0c; 但是我个人实在是懒&#xff0c;每次都要这么弄太累了。想找找hexo有没有对应的插件&#xff0c;还是有的 npm install hexo-filter-auto-spacing --save安装了之后&#xff0c;在hexo的…

Qt生成动态链接库并使用动态链接库

项目结构 整个工程由一个主程序构成和一个模块构成(dll)。整个工程的结构目录如下 Define.priMyProject.proMyProject.pro.user ---bin ---MainProgrammain.cppMainProgram.proMainProgram.pro.userwidget.cppwidget.hwidget.ui ---MathDllMathDll.proMathDll.pro.userMyMath.…