当前位置: 首页 > news >正文

HashMap详解+简单手写实现(哈希表)

1. 什么是 HashMap?

HashMap是Java集合框架中的一种数据结构,它实现了Map接口,用于存储键值对(Key-Value Pair)。HashMap允许null键和null值,并且不保证元素的顺序。

---

2. HashMap 的工作原理

2.1 内部结构
  • 数组 + 链表/红黑树:HashMap内部使用一个数组(称为table)来存储数据,每个数组元素是一个链表或红黑树的头节点。
  • 哈希函数:通过哈希函数将键(Key)映射到数组的索引位置。
2.2 插入数据
  • 计算哈希值:使用键的hashCode()方法计算哈希值。
  • 计算索引:通过哈希值和数组长度计算索引位置。
  • 处理冲突:如果索引位置已经有元素,则通过链表或红黑树处理冲突。
  • 插入数据:将键值对插入到链表或红黑树中。
2.3 查找数据
  • 计算哈希值:使用键的hashCode()方法计算哈希值。
  • 计算索引:通过哈希值和数组长度计算索引位置。
  • 查找数据:在链表或红黑树中查找键值对。
2.4 扩容机制
  • 负载因子:HashMap有一个负载因子(默认0.75),当元素数量超过容量 * 负载因子时,HashMap会进行扩容。
  • 扩容过程:创建一个新的数组,将旧数组中的元素重新哈希到新数组中。

---

3. HashMap 的特点

3.1 优点
  • 快速查找:通过哈希函数,HashMap可以在平均O(1)的时间复杂度内查找元素。
  • 灵活:允许null键和null值。
3.2 缺点
  • 无序:HashMap不保证元素的顺序。
  • 线程不安全:HashMap不是线程安全的,多线程环境下需要使用ConcurrentHashMap。

---

4. 常见问题

4.1 HashMap 和 Hashtable 的区别?
  • HashMap:允许null键和null值,线程不安全。
  • Hashtable:不允许null键和null值,线程安全。
4.2 HashMap 的负载因子为什么是0.75?
  • 负载因子:0.75是时间和空间的一个平衡点,既不会浪费太多空间,也不会导致频繁扩容。
4.3 HashMap 如何处理哈希冲突?
  • 链表:Java 8之前,HashMap使用链表处理冲突。
  • 红黑树:Java 8之后,当链表长度超过8时,HashMap会将链表转换为红黑树,提高查找效率。

---

5. 进一步优化与迭代方向

  • 使用合适的初始容量:根据预估的元素数量设置初始容量,减少扩容次数。
  • 选择合适的负载因子:根据实际需求调整负载因子,平衡时间和空间。
  • 线程安全:在多线程环境下使用ConcurrentHashMap,避免线程安全问题。

6.手写一个哈希表


class HashNode<K,V> {
    //定义哈希表的节点类
    K key;
    V value;
    HashNode<K,V> next;//用于处理哈希冲突的链表
    public HashNode(K key, V value){
        this.key = key;
        this.value = value;
        this.next = null;
    }
    public static void main(String[] args) {
        MyHashMap<String, Integer> map = new MyHashMap<>();
        map.put("Apple", 1);
        map.put("Banana", 2);
        map.put("Orange", 3);

        System.out.println(map.get("Banana")); // 输出: 2
        map.remove("Banana");
        System.out.println(map.get("Banana")); // 输出: null
        System.out.println(map.size()); // 输出: 2
    }
}
//计算键的哈希值
class MyHashMap<K,V> {
    private static final int DEFAULT_CAPACITY = 16;//默认容量
    private static final float LOAD_FACTOR = 0.75f; // 负载因子

    private HashNode<K, V>[] table;//哈希表数组
    private int size;//当前元素数量
    public MyHashMap(){
        table = new HashNode[DEFAULT_CAPACITY];
        size = 0;
    }
    //计算键的哈希值
    private int hash(K key){
        return key == null ? 0 :Math.abs(key.hashCode() % table.length);
    }

    //插入键值对
    public void put(K key,V value){
        int index = hash(key);
        HashNode<K,V> node = table[index];

        //遍历链表,检查是否存在相同的键
        while (node != null){
            if(node.key.equals(key)){
                node.value = value;//更新值
                return;
            }
            node = node.next;
        }
        //插入到新节点到链表头部
        HashNode<K,V> newNode = new HashNode<>(key,value);
        newNode.next = table[index];
        table[index] = newNode;
        size++;
        //检查是否需要扩容
        if ((float) size/table.length > LOAD_FACTOR){
            resize();
        }
    }
    //查找键相对应的值
    public V get(K key){
        int index = hash(key);
        HashNode<K,V> node = table[index];

        //遍历链表,查找键
        while (node != null){
            if (node.key.equals(key)){
                return node.value;
            }
            node = node.next;
        }
        return null;//未找到
    }
    //删除键值对
    public void remove(K key){
        int index = hash(key);
        HashNode<K,V> node = table[index];
        HashNode<K,V> prev = null;


        //遍历链表,查找键
        while (node != null){
            if(node.key.equals(key)){
                if(prev == null){
                    table[index] = node.next;//删除链表头部
                }else {
                    prev.next = node.next;//删除链表的中间或尾部
                }
                size--;
                return;
            }
            prev = node;
            node = node.next;
        }
    }

    //扩容哈希表
    private void resize(){
        int newCapacity = table.length*2;
        HashNode<K,V>[] newTable = new HashNode[newCapacity];

        //重新载入哈希所有元素
        for (HashNode<K,V> node : table){
            while(node != null){
                int newIndex = hash(node.key);
                HashNode<K,V> next = node.next;
                node.next = newTable[newIndex];
                newTable[newIndex] = node;
                node = next;

            }
        }
        table = newTable;
    }

    //获取当前元素数量
    public  int size(){
        return size;
    }


}


文章转载自:

http://0KHx6QRA.fhrgk.cn
http://0T4G72xO.fhrgk.cn
http://IIInLkxC.fhrgk.cn
http://QB5Oubs7.fhrgk.cn
http://CSs6GrLQ.fhrgk.cn
http://YcsZJWnr.fhrgk.cn
http://thoUuf7i.fhrgk.cn
http://SvqRkJwp.fhrgk.cn
http://jw50cQh3.fhrgk.cn
http://l99uBB4k.fhrgk.cn
http://53TWItyB.fhrgk.cn
http://cKVb48xh.fhrgk.cn
http://DniubLM1.fhrgk.cn
http://7uQyqoB6.fhrgk.cn
http://2TX6tQdR.fhrgk.cn
http://Tmg1hGvY.fhrgk.cn
http://iGV5GEsq.fhrgk.cn
http://dGeByi2w.fhrgk.cn
http://PYYVNWSO.fhrgk.cn
http://HFLj4MD9.fhrgk.cn
http://ZQ7AqIUG.fhrgk.cn
http://Ax9hmApD.fhrgk.cn
http://wIH9k808.fhrgk.cn
http://CYG6lgzY.fhrgk.cn
http://vyLyNgZB.fhrgk.cn
http://XPklFhqQ.fhrgk.cn
http://7I3R3Mog.fhrgk.cn
http://7Z3vGghN.fhrgk.cn
http://CsjGGqXK.fhrgk.cn
http://iws3pM7a.fhrgk.cn
http://www.dtcms.com/a/15171.html

相关文章:

  • 深度学习机器学习:常用激活函数(activation function)详解
  • Qt Creator 5.0.2 (Community)用久了突然变得很卡
  • Kafka分区管理大师指南:扩容、均衡、迁移与限流全解析
  • Flutter 实现 iOS 小组件与主 App 的通信
  • make命令学习
  • 知识拓展:设计模式之装饰器模式
  • 传输层协议TCP ( 下 )
  • springboot集成zookeeper的增删改查、节点监听、分布式读写锁、分布式计数器
  • UEFI PI PEI(3. PEI Foundation/PEI Dispatcher)
  • 蓝桥与力扣刷题(108 将有序数组转换成二叉搜索树)
  • 解锁豆瓣高清海报(三)从深度爬虫到URL构造,实现极速下载
  • 网站地址栏怎么变成HTTPS开头?
  • windows平台上 oracle简单操作手册
  • 【SQL server】存储过程模板
  • List对象进行排序
  • 如何提升谷歌SEO排名?
  • Springboot RabbitMQ 消费失败消息清洗与重试机制
  • 超越DeepSeek R1的Moe开源大模型 Qwen2.5-max 和 Qwen Chat Web UI 的发布,阿里搅动AI生态
  • langchain实现的内部问答系统及本地化替代方案
  • Linux相关概念和易错知识点(27)(认识线程、页表与进程地址空间、线程资源划分)
  • Flask与Jinja2模板引擎:打造动态Web应用
  • 什么是平面环形无影光源
  • Netty源码解析之异步处理(二):盛赞Promise中的集合设计
  • 【云平台监控】Prometheus 监控平台部署与应用
  • 2025年SEO工具有哪些?老品牌SEO工具有哪些
  • uniapp PDF 预览和下载
  • 1.14学习总结
  • BFS 走迷宫
  • 蓝桥杯之并查集
  • 滤波总结 波形处理原理 如何对一个规律的波形进行滤波 显现出真正的波形 如何设计滤波