《Java 100 天进阶之路》第47篇:HashMap源码全解(上)(2026版)

2026大厂Java八股文:从背题到工程化思维跃迁 Java工程师能力评估正从语法记忆转向系统级工程实践。JVM性能调优与并发编程已成为大厂技术水位的核心标尺,其原理不仅关乎GC机制、线程模型等底层知识,更深度耦合容器环境、业务语义与故障诊断能力。掌握JVM调优可精准定位Metaspace OOM、Full GC根因;理解高阶并发设计能应对K8s下ForkJoinPool并行度劣化等真实场景。这类能力已广泛应用于电商风控、支付系统、配置中心等关键业务模块,成为高级Java工程师区别于初级开发者的分水岭。本文以2026大厂题库为切口,解析如何将传统八股文转化为 阅读详情

第47篇:HashMap源码全解(上)(2026版)

📌 系列导航:《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第46篇:LinkedList源码与对比 |
➡️ 下一篇:第48篇:HashMap源码全解(下)



一、核心知识点

  • HashMap 底层结构:数组 + 链表 + 红黑树(JDK 8+)
  • 核心属性:table(桶数组)、size(元素个数)、threshold(扩容阈值)、loadFactor(负载因子)
  • 哈希算法:hash() 扰动函数(高16位与低16位异或)
  • 索引计算:(n - 1) & hash(位运算代替取模)
  • put 方法流程:计算 hash → 定位桶 → 插入/覆盖 → 树化/扩容
  • get 方法流程:计算 hash → 定位桶 → 遍历链表/红黑树查找
  • JDK 7 vs JDK 8 核心差异:数据结构、插入方式、扩容优化

二、通俗讲解(1分钟开心学)

1. HashMap 是什么?

HashMap 是一个基于哈希表的键值对容器,通过 key 的哈希值快速定位 value 的存储位置,实现 O(1) 的平均查找和插入性能。

生活类比:
HashMap 就像一个带编号的储物柜(数组),每个柜子里面可能挂着一串钥匙(链表)。你把东西存进去时,工作人员根据你的名字(key)计算出一个柜子编号(hash),直接把东西放到对应柜子里;取东西时,同样根据名字计算编号,直接去那个柜子取,不用翻遍所有柜子。

Hashmap储位柜

2. 为什么要有链表?

不同 key 可能计算出相同的柜子编号(哈希冲突)。HashMap 的解决方案是:每个柜子里放一条链,把冲突的 key-value 串在一起。找东西时,先找到柜子,再顺着链挨个比对 key。

3. 为什么 JDK 8 引入红黑树?

当某个柜子的链太长时,查找效率会从 O(1) 退化成 O(n)。JDK 8 规定:当链表长度 ≥ 8 且数组容量 ≥ 64 时,将链表转换为红黑树,查找效率提升到 O(log n)。

生活类比:
某个柜子的钥匙串越挂越长,每次找钥匙都要从头捋到尾(链表 O(n))。红黑树就像把这串钥匙整理成一棵有规律的树形目录,找起来快多了(O(log n))。

链表

4. HashMap 的扩容机制

当元素个数超过 容量 × 负载因子(默认 16 × 0.75 = 12)时,HashMap 会扩容为原来的 2 倍,并将所有元素重新分配到新数组中。

三、源码核心片段 + 场景说明

3.1 核心属性与构造方法
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    // 默认初始容量:16(必须是2的幂)
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

    // 最大容量:2^30
    static final int MAXIMUM_CAPACITY = 1 << 30;

    // 默认负载因子:0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 链表转红黑树的阈值:8
    static final int TREEIFY_THRESHOLD = 8;

    // 红黑树转链表的阈值:6
    static final int UNTREEIFY_THRESHOLD = 6;

    // 树化的最小数组容量:64
    static final int MIN_TREEIFY_CAPACITY = 64;

    // 存储元素的数组(桶)
    transient Node<K,V>[] table;

    // 元素个数
    transient int size;

    // 扩容阈值(容量 × 负载因子)
    int threshold;

    // 负载因子
    final float loadFactor;
}

💡 为什么容量必须是 2 的幂? 计算索引时使用 (n - 1) & hash 代替 hash % n,位运算更快;且 n - 1 的二进制全是 1,能保证哈希值均匀分布。

Node 内部类结构

static class Node<K,V> implements Map.Entry<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 final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }

    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }

    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

    public final boolean equals(Object o) {
        if (o == this) return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

每个桶中的链表节点就是 Node 对象,包含 hash、key、value 和指向下一个节点的指针。红黑树节点 TreeNode 继承自 LinkedHashMap.Entry,后者又继承自 Node,因此树节点也兼容链表操作。

3.2 哈希算法——扰动函数
static final int hash(Object key) {
    int h;
    // key 为 null 时 hash = 0(所以 HashMap 支持 null key)
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

扰动函数原理:将 key 的 hashCode() 高 16 位与低 16 位进行异或运算。

为什么要扰动? 数组索引计算只取 hash 的低 n 位((n - 1) & hash)。如果只依赖原始 hashCode 的低位,当 hashCode 低位分布不均时,哈希碰撞会非常严重。扰动函数让高 16 位也参与低位计算,使哈希值分布更均匀。

💡 JDK 7 vs JDK 8 扰动函数对比:

  • JDK 7:进行了 4 次右移 + 4 次异或 + 1 次二次哈希(共 9 次位运算),扰动强度大但性能开销高。
  • JDK 8:简化为 1 次右移 + 1 次异或(h ^ (h >>> 16))。

JDK 8 简化扰动的原因是引入了红黑树,即使哈希碰撞较多,链表过长时也会转为红黑树,无需过度依赖扰动函数来减少碰撞。

3.3 索引计算方法
// 计算数组索引:n 为数组长度,hash 为扰动后的哈希值
i = (n - 1) & hash

为什么用 & 代替 %? 当 n 是 2 的幂时,(n - 1) & hash 等价于 hash % n,但位运算比取模快得多。例如 n=16 时,n-1=15(二进制 1111),hash & 1111 只取 hash 的低 4 位,相当于 hash % 16。

3.4 put 方法完整流程

源码入口:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

核心流程(putVal 方法):

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;

    // 步骤1:如果 table 为空,先初始化(第一次 put 触发 resize)
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;

    // 步骤2:计算索引,如果该位置为空,直接插入新节点
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        // 步骤3:该位置已有元素(哈希冲突)
        Node<K,V> e; K k;

        // 3.1 如果第一个节点的 key 与要插入的 key 相同,准备覆盖
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;

        // 3.2 如果该节点是树节点,走红黑树插入
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

        // 3.3 否则遍历链表
        else {
            for (int binCount = 0; ; ++binCount) {
                // 遍历到链表尾部,插入新节点(JDK 8 尾插法)
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    // 如果链表长度 ≥ 8,尝试转为红黑树
                    if (binCount >= TREEIFY_THRESHOLD - 1)
                        treeifyBin(tab, hash);
                    break;
                }
                // 链表中找到相同 key,准备覆盖
                if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }

        // 步骤4:如果找到相同 key,覆盖旧值并返回旧值
        if (e != null) {
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            return oldValue;
        }
    }

    // 步骤5:结构性修改计数 + 1(用于 fail-fast)
    ++modCount;

    // 步骤6:如果元素个数超过阈值,触发扩容
    if (++size > threshold)
        resize();

    return null;
}

put 流程总结:

在这里插入图片描述

⚠️ 注意:treeifyBin 方法内部会检查数组容量是否 ≥ MIN_TREEIFY_CAPACITY(64)。如果容量不足,会先调用 resize() 扩容,而不是直接树化。这是很多面试题的陷阱。

3.5 get 方法流程

源码:

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

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;

    // 步骤1:table 不为空且对应位置有元素
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {

        // 步骤2:检查第一个节点
        if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))
            return first;

        // 步骤3:遍历链表或红黑树
        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;
}

get 流程总结:

  1. 计算 hash(key),定位到桶数组的对应位置
  2. 如果该位置为空,返回 null
  3. 如果第一个节点 key 匹配,直接返回
  4. 否则遍历链表/红黑树,用 equals() 比较 key,找到则返回
3.6 resize 扩容机制

扩容触发条件:size > threshold(threshold = capacity × loadFactor)。

扩容核心逻辑:

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;

    // 1. 计算新容量和新阈值
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        // 新容量 = 旧容量 × 2,新阈值 = 旧阈值 × 2
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1;
    }
    // 初始化场景...
    else {
        newCap = DEFAULT_INITIAL_CAPACITY;  // 16
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 12
    }

    // 2. 创建新数组
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];

    // 3. 将旧数组数据迁移到新数组
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    // 单节点直接重新计算索引
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    // 红黑树节点拆分
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else {
                    // 链表节点:拆分为低位链表和高位链表
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        // 关键判断:(e.hash & oldCap) == 0 留在原位,否则移动 oldCap
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null) loHead = e;
                            else loTail.next = e;
                            loTail = e;
                        } else {
                            if (hiTail == null) hiHead = e;
                            else hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    // 低位链表放在原索引位置
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    // 高位链表放在 (原索引 + oldCap) 位置
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

扩容时为什么元素要么在原位,要么在原位 + oldCap?

由于容量翻倍(从 n 到 2n),索引计算从 (n-1) & hash 变为 (2n-1) & hash。判断 (e.hash & oldCap) == 0 可以快速决定元素位置:

  • 结果为 0:新索引 = 旧索引
  • 结果不为 0:新索引 = 旧索引 + oldCap

举例说明:假设 oldCap = 16(二进制 10000),扩容后 newCap = 32(100000)。原来索引由 hash & 15 决定(低 4 位)。新索引由 hash & 31 决定(低 5 位)。如果 hash 的第 5 位(对应数值 16)是 0,则新索引 = 旧索引;如果是 1,则新索引 = 旧索引 + 16。而 (e.hash & oldCap) 恰好就是检测 hash 的第 5 位是否为 1。

这种设计不需要重新计算每个元素的 hash,极大提升了扩容效率。

四、JDK 7 vs JDK 8 HashMap 核心差异

对比维度JDK 7JDK 8
底层结构数组 + 链表数组 + 链表 + 红黑树
链表插入方式头插法(新节点插入链表头部)尾插法(新节点插入链表尾部)
扰动函数次数9 次位运算(4 次右移 + 4 次异或 + 1 次二次哈希)2 次位运算(1 次右移 + 1 次异或)
扩容后元素迁移重新计算每个元素的 hash通过 (e.hash & oldCap) 判断,拆分为高低位链表
扩容死循环多线程扩容可能死循环尾插法 + 高低位拆分,避免死循环
树化阈值无链表长度 ≥ 8 且数组容量 ≥ 64

⚠️ 多线程安全问题:HashMap 不是线程安全的,多线程环境下使用 ConcurrentHashMap 或 Collections.synchronizedMap。

五、避坑要点

错误/误区后果正确做法
默认容量 16,频繁插入大量数据多次扩容,性能损耗预估数据量,指定初始容量:new HashMap<>(预估容量)
用自定义对象作为 key 但不重写 hashCode()/equals()无法正确获取 value重写 hashCode() 和 equals(),且遵守契约
多线程环境使用 HashMap数据丢失、死循环(JDK 7)、size 不准使用 ConcurrentHashMap
遍历时修改 HashMapConcurrentModificationException用迭代器的 remove() 或 ConcurrentHashMap
负载因子设置过高(如 0.9)哈希冲突增加,查询变慢保持 0.75,除非内存极度受限
依赖 HashMap 的遍历顺序业务逻辑出错HashMap 不保证顺序,用 LinkedHashMap 或 TreeMap

六、面试高频考点

Q1:HashMap 的底层数据结构是什么?

JDK 7:数组 + 链表。JDK 8+:数组 + 链表 + 红黑树。当链表长度 ≥ 8 且数组容量 ≥ 64 时,链表转换为红黑树。

Q2:HashMap 的默认容量和负载因子是多少?为什么是 0.75?

默认容量 16,负载因子 0.75。0.75 是时间和空间的折中选择:太高(如 1.0)哈希冲突多,查询慢;太低(如 0.5)空间浪费严重。

Q3:HashMap 的 put 流程?

① 计算 hash(key) → ② 若 table 为空则初始化 → ③ 通过 (n-1) & hash 计算索引 → ④ 若桶为空直接插入 → ⑤ 若桶不为空,比较 key,相同则覆盖;若是树节点走红黑树插入;若是链表则遍历,找到相同 key 则覆盖,否则尾插法插入 → ⑥ 若链表长度 ≥ 8 尝试树化(还需检查数组容量 ≥ 64,否则先扩容)→ ⑦ size++,若超过阈值则扩容。

Q4:扰动函数的作用是什么?

将 hashCode() 的高 16 位与低 16 位异或,让高位参与低位计算,使哈希值分布更均匀,减少哈希碰撞。

Q5:HashMap 为什么容量必须是 2 的幂?

① 计算索引用 (n-1) & hash 代替 hash % n,位运算更快;② n-1 的二进制全是 1,保证哈希值均匀分布;③ 扩容时元素迁移只需判断高位,无需重新 hash。

Q6:JDK 8 为什么引入红黑树?

当链表过长时,查找效率退化为 O(n)。红黑树的查找效率为 O(log n),能保证极端情况下的性能。

Q7:JDK 7 和 JDK 8 的 HashMap 有什么区别?

① JDK 8 引入红黑树;② JDK 7 头插法,JDK 8 尾插法;③ JDK 7 扰动 9 次位运算,JDK 8 扰动 2 次;④ JDK 8 扩容时通过 (e.hash & oldCap) 判断元素位置,效率更高。

Q8:HashMap 和 Hashtable 的区别?

HashMap 线程不安全,允许 null key 和 null value;Hashtable 线程安全(方法 synchronized),不允许 null。Hashtable 是遗留类,新代码用 HashMap 或 ConcurrentHashMap。

Q9:HashMap 支持 null key 吗?

支持。null key 的 hash 值为 0,存放在数组第 0 个位置。

七、练习题

  1. 源码推导:初始容量 16,负载因子 0.75,插入第几个元素时会触发第一次扩容?

    💡 答案:第 13 个。阈值 = 16 × 0.75 = 12,size 从 12 变为 13 时触发扩容。

  2. 代码分析:以下代码有什么问题?

    HashMap<MyKey, String> map = new HashMap<>();
    map.put(new MyKey(1), "value");
    map.get(new MyKey(1)); // 返回 null
    

    💡 思路:MyKey 没有重写 hashCode() 和 equals(),两个内容相同的 MyKey 对象会产生不同的哈希值,导致定位到不同桶,get 返回 null。必须同时重写 hashCode() 和 equals() 才能正常工作。

  3. 场景设计:如果确定要存储 1 万条数据,如何初始化 HashMap 能获得最佳性能?

    💡 思路:new HashMap<>(10000 / 0.75 + 1),预估容量时考虑负载因子,避免频繁扩容。实际可用 new HashMap<>(15000) 或 new HashMap<>(20000)。

  4. 对比分析:为什么 JDK 8 中 HashMap 的扰动函数从 9 次位运算简化到 2 次?

    💡 思路:JDK 8 引入了红黑树,即使哈希碰撞较多,链表过长时会转为红黑树,查找效率从 O(n) 提升到 O(log n),因此不需要过度依赖扰动函数来减少碰撞。

📊 你的学习进度

  • 当前:第47篇 / 共108篇 · 进阶篇:集合框架源码解析(第45~50篇)
  • ✅ 已完成:基础篇44篇 + 第45~47篇
  • 📖 正在学:第47篇
  • ⏳ 待学习:第48~108篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

👉 下一篇文章预告

《第48篇:HashMap源码全解(下)》

内容简介:红黑树转换机制(treeifyBin)、扩容 resize 深度剖析、JDK 7 多线程死循环问题、JDK 7 vs JDK 8 完整对比、常见面试题深度解析。

💡 学完这篇,你将彻底拿下 HashMap 源码,成为真正的 Java 集合专家。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!

2026 Java面试本质:从八股文到系统性工程能力跃迁 Java面试已不再是语法记忆与API复述,而是对JVM运行时机制、Spring平台级抽象、分布式系统协同等底层原理的综合考察。其核心在于理解Java内存模型(JMM)与JVM内存结构的本质差异,掌握Spring Boot自动装配中@Import与DeferredImportSelector的协作逻辑,并能基于真实生产问题(如OOM根因、ThreadLocal泄漏、MySQL死锁链)开展跨栈诊断。技术价值体现在将单点知识转化为可权衡的架构决策能力,典型应用场景包括高并发订单一致性分析、响应式栈迁移评估、JDK 阅读详情

相关推荐

Flutter/Rust/Java开发者必看:4个2026年实战验证的高效工具

在现代多语言工程实践中,开发者常面临依赖解析卡顿、AI生成代码不可控、JVM内存配置玄学、Rust集合性能瓶颈等基础性挑战。这些本质是构建系统与运行时协同失效的表现,需从编译器兼容性、AST语义约束、GC行为反馈、内存布局优化等底层原理切入。其技术价值在于将经验驱动调优转化为可观测、可回滚、可自动化的工程能力,广泛应用于Flutter跨端构建、Rust嵌入式开发、Java微服务升级及CI/CD流水线加速等核心场景。本文聚焦2026年5月真实验证的4个高活性开源工具,覆盖Dart 3.5+生态适配、Rust

weixin_33675507的博客 522

《Java 100 天进阶之路》第48篇:HashMap源码全解(下)(2026版)

摘要:上篇已掌握核心属性与put/get流程,本篇攻克剩余难点:树化机制(链表≥8且容量≥64→红黑树,容量<64→先扩容)、退链表(节点≤6退为链表)、JDK 7扩容死循环(头插法+多线程→环形链表,附步骤图解)、JDK 8修复(尾插法+高低位拆分)、split红黑树拆分(低位留原位,高位移oldCap)、computeIfAbsent陷阱(HashMap不保证原子性,非幂等函数可能导致重复扣款)、极端场景(所有key hash相同→链表O(n)/红黑树O(log n))。

折哥|智能物流与Java实战,副高数据库工程师。深耕出版社物流WMS/OMS(线体/立库/VGA),也分享Java进阶与OMS电商对接。Oracle→PG实战派。相信沉淀可迁移能力,分享通用技术,不藏不虚。 994

Mythos:首个具备自主攻防闭环能力的AI安全模型

AI安全正从‘用AI辅助安全’迈向‘AI即攻击面’的新阶段。自主攻防闭环能力指模型无需人工提示或预设工具,即可完成目标分析、漏洞识别、利用链构造与执行验证的全周期推理。其技术基础在于状态空间探索与推理时计算扩展,突破传统模式匹配局限,实现对软件运行时行为的深度建模。该能力已实测应用于内核级零日挖掘、微服务RCE自动化生成等高阶场景,推动DevSecOps流程重构与防御体系升维。本文聚焦Claude Mythos所代表的这一代通用安全智能体的核心原理与工程落地逻辑。

weixin_30443813的博客 351

《2026全新 | Java 100 天进阶之路:从零基础到上岗就业,108篇完整学习地图》

摘要:不背八股文,不堆概念。44篇基础 + 64篇进阶(共108篇),100天助你达到Java就业水平,从容面对技术面试。 👉 零基础入门 / 校招社招突击 / 1-3年Java后端提升 / 想系统复盘全栈知识的开发者。

折哥|智能物流与Java实战,副高数据库工程师。深耕出版社物流WMS/OMS(线体/立库/VGA),也分享Java进阶与OMS电商对接。Oracle→PG实战派。相信沉淀可迁移能力,分享通用技术,不藏不虚。 9681

一分钟理解 AP(Affinity Propagation) 亲和⼒传播算法

这篇博客发出来后,我用 Rust 复现代码出现问题。为此,我对对照了 sklearn 的相关代码,反复比较了两天,发现一处 bug,把 += 误写成了 =,导致数据量大的时候完全无法聚类。这个debug过程让我仔细梳理的 AP 算法的计算过程,今天我对这篇博客做了大规模修改,以便能原样展现原论文思想。

程序猿视角 3210

AnyLogic 建立谢林模型

AnyLogic 建立谢林模型说明创建一个新模型创建代理人群体和离散空间双击 people 前的图标,编辑 Person设置 Agent 的形状(Person)在模型中启用离散时间,实现代理行为点击 Person,进行编辑点击运行按钮效果图 说明 我们将把 Schelling 的模型实现为一个基于离散空间/离散时间的代理模型。空间代表一个城市,每个单元格代表一个房子。 agent 是人,是两种颜色:黄色和红色。 最初,人是随机分布在城市中的。人比房子少,所以总是有一个人可能会移动。 这个模型中的 agent

qq_45642410的博客 2575

Java 100天进阶之路|集合框架源码通关总结(2026版)

摘要:第45~50篇集合专题正式收官!一张学习路线图 + 六张速查表 + 14道高频面试题 + 11条云原生避坑清单,系统覆盖 ArrayList 1.5倍扩容、LinkedList 内存开销、HashMap 红黑树树化条件、JDK7死循环、ConcurrentHashMap 桶级锁与协助扩容、BlockingQueue 双锁设计。新增 JDK 8+ 原子方法(compute/merge)、容器化环境无界队列 OOM 防护、LongAdder 伪共享规避等 2026 实战加分项。从“调用API”到“吃透源码

折哥|智能物流与Java实战,副高数据库工程师。深耕出版社物流WMS/OMS(线体/立库/VGA),也分享Java进阶与OMS电商对接。Oracle→PG实战派。相信沉淀可迁移能力,分享通用技术,不藏不虚。 553

蚁群算法(Ant Colony Optimization,ACO)讲解+代码实现

通常情况下,较优的解对应的路径上的信息素会被加强,而较差解对应的信息素则会减少或蒸发。的化学物质,这种物质会在蚂蚁走过的路径上留下痕迹,后续的蚂蚁会根据这些信息素的浓度来选择路径,从而形成一条从蚁巢到食物源的。在求解TSP蚁群算法中,每只蚂蚁相互独立,用于构造不同的路线,蚂蚁之间通过信息素进行交流,合作求解。的过程,该路段经过的蚂蚁越多,信息素留下的就越多,浓度越高,更多的蚂蚁都会选择这个路段。需要注意的是,蚁群算法是一种。(6)计算每只蚂蚁的总距离:遍历每只蚂蚁的路线,累加经过的城市之间的距离。

m0_73804764的博客 1万+

《Java 100 天进阶之路》第46篇:LinkedList源码与对比(2026版)

摘要:从双向链表底层结构到源码级解析,深入拆解 `Node` 节点、`linkFirst/linkLast`、`node(index)` 二分查找优化。全面对比 ArrayList vs LinkedList——时间复杂度、内存开销、使用场景,**破除“LinkedList 插入一定更快”的误区**(中间插入两者均为O(n),但性能量级不同)。覆盖生产避坑、面试高频题(含 ArrayDeque 选型、transient 自定义序列化),附完整对比表和练习题。一篇理清 List 选型,面试不再混淆。

折哥|智能物流与Java实战,副高数据库工程师。深耕出版社物流WMS/OMS(线体/立库/VGA),也分享Java进阶与OMS电商对接。Oracle→PG实战派。相信沉淀可迁移能力,分享通用技术,不藏不虚。 599

告别轮询!手把手教你用S32K3的FlexCAN Enhanced FIFO+DMA实现高效CAN FD数据接收

本文详细介绍了如何利用S32K3芯片的FlexCAN Enhanced FIFO与DMA组合方案,实现高效CAN FD数据接收,彻底告别传统轮询和中断模式。通过深度缓冲、智能过滤和DMA联动技术,该方案在5Mbps高负载场景下将CPU占用率从42%降至3%以下,显著提升系统实时性和吞吐量。

weixin_30906185的博客 499

多旋翼飞行器设计与控制·Pixhawk4以及外部设备接线(笔记004)

笔记主要来源:Pixhawk 4 接线快速入门 目录 一、Pixhawk 4 的各个接口 二、接线图概览(重要传感器及外围设备) 三、飞控、GPS的安装和方向 四、电源 五、无线电遥控 六、数传电台(可选) 七、SD Card(可选) 八、电机以及其他外设 一、Pixhawk 4 的各个接口 FMU:Flight Management Unit 飞行管理组件 主 FMU 处理器:STM32F765 32 位 Arm® Cortex®-M7,216MHz,2MB...

猪猪的博客 1万+

2026-W1 周总结(Lab 01-06)

Java 基础底层 6 连击:手撕 ArrayList / HashMap / equals 协定 / 反射 / 注解 / SPI。从源码到面试问答,每周 1-2 小时的可落地实操。

脾气很好的菜昂 158

携程机票数据爬取实战:使用 Python 获取携程网机票信息

通过本篇博客,我们详细介绍了如何使用 Python 爬取携程网的机票信息,涉及了静态网页爬取(使用 Requests 和 BeautifulSoup)和动态网页爬取(使用 Selenium)两种方法。通过获取的数据,我们不仅能够进行票价分析,还可以构建更为复杂的预测模型或趋势分析工具。爬虫技术不仅能够帮助我们收集有价值的数据,还能够为商业决策提供数据支持。在实际应用中,我们可以结合更多的分析方法,如自然语言处理、图像处理等,进一步提升数据的价值。

2201_76125261的博客 1652

2026年Java面试题

AOF和RDB是两种redis持久化的机制。 重写和重载的区别 ssm原理 servlet生命周期 自定义注解说一说 sleep和wait的区别 map的遍历方式有几种 线程有几种创建方式 说一下图论算法 int有4个字节 mysql的联合索引 linux怎么查看端口号 netstat -tunpl | grep 端口号 fegin怎么暴露出来 fegin有哪些注解 hashMap不是线程安全的,ConcurrentHashMap 是线程安全的 redis和mysql的事物有那么一些不一样 redis幂等

qq_21277357的博客 3348

从数据读取到业务事件:理解 ABAP CDS 视图的目的与选型(含 RAP、扩展与分析场景)

CDS视图在SAP S/4HANA中不仅是数据查询工具,更是语义模型和运行时能力的载体。它通过分层架构(基础视图、组合视图、消费视图等)实现数据复用与场景适配,支持UI、分析、事务处理等多样化消费。视图类型包括提供数据访问的接口视图(I_前缀)、消费视图(C_前缀)和事务视图,以及不提供数据访问的扩展视图。这种分层设计确保了数据模型的稳定性与扩展性,同时支持自定义字段和上下文感知分析。正确理解CDS视图的用途和分层规则,能帮助开发者构建更健壮、可维护的SAP应用。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 297

PCL点云库入门(第1讲)——PCL库简介和安装(Win10/11+VS2019+PCL 1.12.0)

PCL,全称为,即点云库,是一个专注于2D/3D图像和点云处理的大型开源项目。该项目最初由斯坦福大学的杰出计算机视觉专家Radu博士及其团队开发和维护,经过十余年的发展,目前(2024年12月)已更新至1.14.1版本。PCL点云库集成了众多尖端的点云处理算法,这些算法不仅包括基础的滤波、特征估计、表面重建、配准、模型拟合和分割等,还涵盖了更高级的功能,如点云数据的语义理解、场景解析以及动态环境下的实时处理。这些算法能够对含有噪声的点云数据进行有效过滤,去除异常值;

qq_36812406的博客 1万+

2026年Java开发者突围指南:源码、云原生与性能优化

Java作为企业级开发的主流语言,其技术生态持续演进。理解JVM原理和并发编程机制是深入掌握Java的基础,从垃圾回收算法到内存模型的设计,这些底层知识直接影响应用性能。随着云原生技术的普及,Java开发者需要适应从传统框架到Quarkus、GraalVM等现代化工具的转型,这涉及到容器化、服务网格等云原生概念。性能优化能力成为区分开发者水平的关键,通过Arthas、JFR等工具进行生产环境诊断,可以解决线程池配置、GC策略等典型问题。本文结合2026年Java技术趋势,探讨如何通过源码研读、云原生技术栈重

weixin_30582943的博客 172

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

2026最新 Springboot+Layui英语单词学习系统的设计与实现

全栈开发项目源码分享 本文分享一个基于SpringBoot+LayUI+MyBatis的完整项目源码,包含前后端实现。项目由985高校毕业、现任大厂高级开发工程师提供,涵盖Java、小程序、前端等技术栈。源码包含数据库文件、详细说明文档及演示视频,适合毕设选题和项目实战参考。 技术框架说明: SpringBoot:简化Spring应用开发,提供自动配置和内嵌服务器支持 LayUI:轻量级前端UI框架,采用模块化设计理念 MyBatis:持久层框架,支持动态SQL和缓存机制 项目特点: 完整可运行的源码 清晰

Hypoxia_9的博客 597

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

2026面试题汇总(已完结)

手撕字节跳动面试时出现过的算法题 1.买股票的最佳时机 leetcode 原题链接:买股票的最佳时机 题目: 121. 买卖股票的最佳时机 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。 代码: class Solution { public: int maxProfit(vector<int>& prices) { int minDat

qq_23923713的博客 1243

金九银十,如何远程面试拿下大厂offer?(附大厂面经+面试宝典)

前言 “找工作 3 个多月了,还没有遇到合适的,坐标杭州。”“坐标北京,2 年工作经验,裸辞 1 个月了,Java/Python 方向都在找,投的简历都石沉大海了。”“金三银四找的全是 996 的,双休只有外企和非互联网行业。”“去年冬天被裁员的,今年到现在还没找着像样的工作。”“投了半个多月简历,一个面试机会都没有,送达,已读。” 在过去得2019里,这是我在互联网里听过最多的说辞,对于程序员找工作,行情似乎并不那么乐观,在这群人中,,不乏多年工作经验的资深程序员,也有入行不久的新人程序员。今年的

gp0001111的博客 285

Caffeine 核心设计决策不变量速查:驱逐、过期、并发、刷新与自适应窗口的工程准则

Caffeine([README.md](https://link.gitcode.com/i/9376cf6a9450f6a1f5be1cab25f96e22))是一个面向 Java 11+ 的高性能缓存库,其内部机制(TinyLFU 准入、分层过期、自适应窗口攀爬器、写缓冲)包含大量"看似可疑、实为有意为之"的边界语义。本文基于仓库内 [.claude/rules/design-decisio

gitblog_01045的博客 776

AI编程工具选型指南:团队协作效率的四维决策矩阵

AI编程助手已从个人效率工具演进为团队协作基础设施的核心组件。其本质是通过代码理解、上下文建模与知识沉淀,降低软件开发中的‘协作熵’——即因信息不对称、知识孤岛和上下文断裂导致的沟通成本与返工损耗。TRAE强化团队知识图谱,Cursor重构结对编程范式,GitHub Copilot保障企业合规落地,Claude Code提供本地化隐私可控能力。在微服务架构普及、多技术栈并存、新人快速迭代的当下,选择标准不再仅看生成准确率,而需综合评估跨文件引用稳定性、隐性知识显性化程度、突发协作中断恢复速度及开发者信任度。

weixin_34406086的博客 383

RabbitMQ Consistent Hash Exchange(x-consistent-hash)插件完全指南:原理、权重配置与多语言实战

> 本文基于 `rabbitmq-server` 仓库中 [`deps/rabbitmq_consistent_hash_exchange`](https://link.gitcode.com/i/d7174b7b819b47cf433741acb4e544ae) 插件的官方文档([README.md](https://link.gitcode.com/i/048197de27f7aa86f1a9

gitblog_00612的博客 877

工作中使用到的单词(软件开发)

1.エイリアス   alias 别名 powershell使用 get-help 命令时,会得到与此命令 相关(别名)的命令 2.プロキシ proxy 代 理 3.プロモート   promote 促进,提升   开发的系统新功能上线发布,有时会使用到这个词,比如 STプロモート 还有使用リリース这个词的。比如STリリース。个人认为两者含义相同。 4.見逃し み................................................................

sun0322 6万+

Blender 第三方许可证全景解读:从依赖合规清单到 `make license` 自动化生成机制

本文以 [release/license/license.md](https://link.gitcode.com/i/a1a6e58d5d45c2a2a7aedf6c053bbc30) 为核心,系统解读 Blender 的第三方依赖合规体系:涵盖 Blender 自身的 GPL-3.0-or-later 许可、随附字体栈、22 类许可证下数十个第三方库的版本与版权清单,并深入剖析该清单由 [to

gitblog_00285的博客 448

01:SpringAI

ai

weixin_43761434的博客 410
上一篇: 面试官问:重载和重写有什么区别?(附图解+比喻+避坑指南)
下一篇: 《Java 100 天进阶之路》第48篇:HashMap源码全解(下)(2026版)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值