HsahMap源码分析(jdk8)

本文详细解析了HashMap的工作原理,包括其内部结构、初始化参数的选择、rehash操作的影响以及线程安全问题。同时,介绍了如何通过调整初始容量和加载因子来优化性能。

有帮助的话对Micro极微完美女装品牌店淘宝店支持支持包

一、HashMap的解释(jdk)

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

HashMap 的实例有两个参数影响其性能:初始容量加载因子容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。

如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。

HashMap是非线程安全的,要想使其变为线程安全的需要使用 Collections.synchronizedMap 方法来“包装”该映射,最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:

   Map m = Collections.synchronizedMap(new HashMap(...));

二、comparableClassFor(Object x)comparableClassFor(Object x)

这里直接看这位大哥的文章,写得很仔细https://blog.youkuaiyun.com/qpzkobe/article/details/79533237

三、isEmpty()

isEmpty(): 如果此映射不包含键-值映射关系,则返回 true。

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

public int size() {
        return entrySet().size();
    }

entrySet().size()方法是用来判断映射个数,如果没有则返回0,我们通过isEmpty()方法判断size()返回个数是否为0,如果为0,则返回true。

四、entrySet()

entrySet():返回此映射所包含的映射关系的 Set 视图。

五、containsValue()

containsValue():如果此映射将一个或多个键映射到指定值,则返回 true。

public boolean containsValue(Object value) {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (value==null) {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getValue()==null)
                    return true;
            }
        } else {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (value.equals(e.getValue()))
                    return true;
            }
        }
        return false;
    }

从上面代码我们可以看出这个value分为两种情况,null和非null,我们构建一个迭代器,通过迭代器对这个映射关系进行遍历,如果存在则返回true不存在返回false。

六、get()

get(): 返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

 

稍后====

 

 

 

 

内容概要:文章以“智能网页数据标注工具”为例,深入探讨了谷歌浏览器扩展在毕业设计中的实战应用。通过开发具备实体识别、情感分类等功能的浏览器扩展,学生能够融合前端开发、自然语言处理(NLP)、本地存储与模型推理等技术,实现高效的网页数据标注系统。文中详细解析了扩展的技术架构,涵盖Manifest V3配置、内容脚本与Service Worker协作、TensorFlow.js模型在浏览器端的轻量化部署与推理流程,并提供了核心代码实现,包括文本选择、标注工具栏动态生成、高亮显示及模型预测功能。同时展望了多模态标注、主动学习与边缘计算协同等未来发展方向。; 适合人群:具备前端开发基础、熟悉JavaScript和浏览器机制,有一定AI模型应用经验的计算机相关专业本科生或研究生,尤其适合将浏览器扩展与人工智能结合进行毕业设计的学生。; 使用场景及目标:①掌握浏览器扩展开发全流程,理解内容脚本、Service Worker与弹出页的通信机制;②实现在浏览器端运行轻量级AI模型(如NER、情感分析)的技术方案;③构建可用于真实场景的数据标注工具,提升标注效率并探索主动学习、协同标注等智能化功能。; 阅读建议:建议结合代码实例搭建开发环境,逐步实现标注功能并集成本地模型推理。重点关注模型轻量化、内存管理与DOM操作的稳定性,在实践中理解浏览器扩展的安全机制与性能优化策略。
JDK1.7 中 `HashMap` 的 `transfer` 方法用于在扩容时将旧表中的元素转移到新表中。以下是 `transfer` 方法的源码及分析: ```java void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry<K,V> e : table) { while(null != e) { Entry<K,V> next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } } } ``` ### 代码逐行分析 1. `int newCapacity = newTable.length;`:获取新表的容量,后续会使用该容量来计算元素在新表中的位置。 2. `for (Entry<K,V> e : table)`:遍历旧表中的每个位置,`table` 是旧的哈希表数组。 3. `while(null != e)`:对于旧表中每个位置上的链表,遍历其中的每个元素。 4. `Entry<K,V> next = e.next;`:保存当前元素的下一个元素,因为后续操作会改变当前元素的 `next` 引用。 5. `if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); }`:如果需要重新计算哈希值(`rehash` 为 `true`),则重新计算当前元素的哈希值。若 `key` 为 `null`,哈希值为 0;否则调用 `hash` 方法计算哈希值。 6. `int i = indexFor(e.hash, newCapacity);`:根据元素的哈希值和新表的容量,计算元素在新表中的位置。 7. `e.next = newTable[i];`:将当前元素的 `next` 引用指向新表中该位置的元素,采用头插法将元素插入新表。 8. `newTable[i] = e;`:将当前元素放到新表的该位置。 9. `e = next;`:将当前元素更新为之前保存的下一个元素,继续遍历链表。 ### 并发问题 JDK1.7 中当两个线程同时操作 `HashMap` 扩容时,如果有一个线程挂起,而另一个线程顺利执行,会造成循环链表的问题。例如,假设线程 A 执行到 `e.next = newTable[i];` 时挂起,此时 `e.next = null`,可能会导致后续链表结构混乱,形成循环链表 [^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子鞋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值