【Java基础】HashMap底层实现原理 附源码分析及面试题,带你深入Java集合

【Java集合夜话】第6篇:HashMap的底层原理与实现,一文吃透最常用的数据结构 本文深入剖析了Java程序员最常用的HashMap数据结构。通过图解方式,详细讲解了HashMap的底层实现原理,包括哈希函数设计、红黑树优化、扩容机制等核心知识点。文章不仅介绍了JDK7到JDK8的重要升级,还总结了实际开发中的最佳实践和性能优化技巧。此外,还整理了面试中的高频考点,结合源码分析给出了详细解答。无论你是想深入理解HashMap原理,还是准备面试,这篇文章都值得细读。 阅读详情

Java8的HashMap实现和之前的版本有所不同,本文中将作出详尽的说明。另外对并发环境下的ConcurrentHashMap也做了分析。

1. HashMap的数据结构

在讨论HashMap的存储数据结构之前,先假想两种数据结构:数组和链表。
虽然都可以实现对数据的存储,但这两者基本上是两个极端。

1.1 数组和链表

数组存储区间是连续的,占用内存严重,故空间复杂度很大。但数组的查找时间复杂度小,能直接锁定存储地址中的元素,所以时间复杂度为O(1);数组的特点是:寻址容易,插入和删除困难;
链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。

1.2 哈希

那么存不存在具备综合两者的特性,即寻址容易,插入删除也容易的数据结构呢?答案是肯定的,这就是我们要提起的哈希表。哈希表((Hash table)既满足了查找的迅速(时间复杂度为O(1)),同时不占用太多的内存空间,使用十分方便。
哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法—— 拉链法,我们可以理解为“链表的数组” ,如图:
在这里插入图片描述
从下图可以出看出,哈希是由数组和链表组成的。,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%length获得,也就是元素的key的哈希值对数组长度取模得到。比如下图哈希表中,12%16=12,28%16=12,60%16=12,所以12、28及60都存储在数组下标为12的位置。
在这里插入图片描述
事实上,在JDK1.8之前,HashMap采用位桶+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里(如上图所示)。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值8时,将链表转换为红黑树(如下图所示),这样大大减少了查找时间。
在这里插入图片描述
首先HashMap里面实现一个接口Entry,其重要的属性有 key , value,由此我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。
并且,HashMap中维护了一个不参与序列化(由关键字transient保证)的位桶数组,在第一次使用的时候会初始化,附上源码:

transient Node<K,V>[] table;

同时数组元素Node<K,V>实现了Entry接口:

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;
        }
    }

JDK1.8对HashMap的底层实现增加了红黑树的数据结构:

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }

        /**
         * Returns root of tree containing this node.
         */
        final TreeNode<K,V> root() {
            for (TreeNode<K,V> r = this, p;;) {
                if ((p = r.parent) == null)
                    return r;
                r = p;
            }
        }
        /* 源码篇幅偏长,此处省略,建议感兴趣的同学直接去阅读jdk */
   }

2. HashMap的存取机制

2.1 HashMap的put

先附上put(key,value)源码:

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

    /**
     * Implements Map.put and related methods.
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

接下来以流程图的形式简单说下HashMap的put(key,value)的过程:
在这里插入图片描述
Tips:

可能有同学会存在疑问:如果两个key通过hash%Entry[].length得到的index相同,会不会有覆盖的危险?
  这里HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方, 第一个键值对A进来,通过计算其key的hash得到的index=0,记做:Entry[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,Entry[0] = B,如果又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。所以疑问不用担心。也就是说数组中存储的是最后插入的元素。

2.1 HashMap的get

接下来再附上get(key)源码:

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

    /**
     * Implements Map.get and related methods.
     *
     * @param hash hash for key
     * @param key the key
     * @return the node, or null if none
     */
    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;
    }

get(key)方法时获取key的hash值,计算hash&(n-1)并获取在位桶数组中的位置first=tab[hash&(n-1)],Java会始终先检查第一个节点的key是否与参数key相等,不等就判断第一个节点first的下一节点的类型,如果是红黑树的话,就按照红黑树的遍历find()找到对应的Entry,否则就遍历后面的链表找到相同的key值返回对应的Value值即可。
Tips:

注意HashMap计算索引的时候用到了按位取与:(n - 1) & hash,作用相当于取余%(因为&运算返回的值一定小于等于两个数的最小值)。这就意味着数组下标相同,并不表示hashCode也相等。

3. HashMap的扩容机制

加载因子loadFactor(默认0.75):为什么需要使用加载因子,为什么需要扩容呢?因为如果填充比很大,说明利用的空间很多,如果一直不进行扩容的话,链表就会越来越长,这样查找的效率很低,因为链表的长度很大(当然最新版本使用了红黑树后会改进很多),扩容之后,将原来链表数组的每一个链表分成奇偶两个子链表分别挂在新链表数组的散列位置,这样就减少了每个链表的长度,增加查找效率。
构造hash表时,如果不指明初始大小,默认大小为16(即Node数组大小16),如果Node[]数组中的元素达到(加载因子*Node.length)重新调整HashMap大小 变为原来2倍大小,要注意,HashMap扩容是个很耗时的过程。
附上JDK1.8的源码:

/**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     *
     * @return the table
     */
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        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 { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            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;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

4. HashMap的遍历方式

直接附代码:

/**
 * @author Carson
 * @date 2020/1/16 14:15
 */
public class Main {
    public static void main(String[] args) {
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("username", "carson");
        hashMap.put("password", "carson2016");

        /* HashMap的遍历方式1.1 */
        Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
        for (Map.Entry<String, String> set : entrySet) {
            System.out.println(set.getKey() + "=" + set.getValue());
        }
        /* HashMap的遍历方式1.2 */
        Iterator<Map.Entry<String, String>> entryIterator = hashMap.entrySet().iterator();
        while (entryIterator.hasNext()) {
            Map.Entry<String, String> next = entryIterator.next();
            System.out.println("key=" + next.getKey() + " value=" + next.getValue());
        }
        
        /* HashMap的遍历方式2 */
        Iterator<String> iterator = hashMap.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            System.out.println("key=" + key + " value=" + hashMap.get(key));

        }
    }
}

个人建议使用第一种 EntrySet 进行遍历,因为该方案可以把 key/value 同时取出,第二种还得需要通过 key 取一次 value,效率稍低。

5. HashMap小结

5.1 哈希碰撞的影响

你可能还知道哈希碰撞会对hashMap的性能带来灾难性的影响。在JDK1.8之前,如果多个hashCode()的值落到同一个桶内的时候,这些值是存储到一个链表中的。最坏的情况下,所有的key都映射到同一个桶中,这样hashmap就退化成了一个链表——查找时间从O(1)到O(n)。
随着HashMap的大小的增长,get()方法的开销也越来越大。由于所有的记录都在同一个桶里的超长链表内,平均查询一条记录就需要遍历一半的列表。
JDK1.8HashMap的红黑树是这样解决的:

如果某个桶中的记录过大的话(当前是TREEIFY_THRESHOLD = 8),HashMap会动态的使用一个专门的treemap实现来替换掉它。这样做的结果会更好,是O(logn),而不是糟糕的O(n)。
它是如何工作的?前面产生冲突的那些KEY对应的记录只是简单的追加到一个链表后面,这些记录只能通过遍历来进行查找。但是超过这个阈值后HashMap开始将列表升级成一个红黑树,使用哈希值作为树的分支变量,如果两个哈希值不等,但指向同一个桶的话,较大的那个会插入到右子树里。如果哈希值相等,HashMap希望key值最好是实现了Comparable接口的,这样它可以按照顺序来进行插入。这对HashMap的key来说并不是必须的,不过如果实现了当然最好。否则在出现严重哈希碰撞的时候很难获得性能提升。

5.2 HashMap和Hashtable的区别

HashMap和Hashtable(顺带一提,这里的Hashtable的"t"确实是小写,不符合驼峰式命名法)都实现了Map接口,但是它们在线程安全性,同步(synchronization),以及速度上都有着区别:

  • Hashtable底层有监视器锁synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable,而HashMap不能在多个线程之间共享;

  • HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行;

  • HashMap的迭代器(Iterator)是fail-fast(即时失败)迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器Iterator本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常;

  • 由于Hashtable是底层维护了监视器锁,所以在单线程环境下它比HashMap要慢。

5.3 HashMap插入key-value的时候,当两个对象的hashCode相同会发生什么?如果两个键的hashCode相同,又该如何获取值对象?

  1. 首先要明确,当两个对象的hashCode相同时,这两个对象未必相等(如果equals()方法判断也相等,则插入失败)。hashCode值相等时,说明两个对象在HashMap中bucket桶中的位置相同,所以会发生碰撞,因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。当链表的长度超过8的时候,链表会转换成红黑树。
  2. 当我们调用get()方法,HashMap会使用键对象的hashCode找到bucket位置,然后会调用key.equals()方法去找到链表(由于HashMap在链表中存储的是键值对)中正确的节点,最终找到要找的值对象,这里可参看上述get(key)源码中的处理。

5.4 多线程条件下重新调整HashMap大小存在什么问题?

会产生竞态条件(race condition),因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就会形成环形链表,发生死循环了。
回答到这里,也许你会觉得很满意,但是你想过没有,多线程条件下你为什么要选择HashMap?
并发条件下有个安全的HashMap可供选择,即:ConcurrentHashMap。
关于ConcurrentHashMap请参看我的另一篇博客。

参考阅读:
HashMap实现原理分析
Java中HashMap底层实现原理(JDK1.8)源码分析
HashMap底层实现原理及面试问题

数字IC设计技能篇之如何在RTL级降低功耗? 以上就是本次降低芯片功耗的部分心得和收获,由于是在芯片设计阶段还没办法后端直接给出power的报告,前端一般使用spyglass和fsdb来进行功耗分析,后续会更新如何使用spyglass在前仿阶段进行power的分析,以及持续更新有关RTL级降低功耗的相关内容。 阅读详情

相关推荐

电机控制个人学习路线及专业书籍资源分享

个人学习电机控制的学习路线,和相关专业书的电子文档分享

weixin_45182459的博客 9914

Java基础常见面试题——HashMap最新总结(JDK1.8)

HashMap源码

追求幸福,探索未知的博客 2606

[Adaptive Autosar]深入理解--Time Synchronization

目录 1.TS典型应用场景 2.CP和AP时间同步对比 3.时间同步基础知识 4.TBR部分参数说明 5. TS功能说明 5.1 基本说明 5.2 时间纠正计算说明 5.3 时间验证 Time Synchronization 时间同步,后面简称TS 1.TS典型应用场景 时间同步,典型应用就是在自动驾驶领域,自动驾驶领域涉及到各个传感器的数据采集,如IMU采集当前车身姿态和运动信息,毫米波雷达-超声波雷达-环视摄像头-前视摄像头...等等传感器捕获的数据,在算法中需要精确知道每个数.

AgingMoon的博客 3997

HashMap的工作原理

HashMap的工作原理是近年来常见的Java面试题。几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别 ,那么为何这道面试题如此特殊呢?是因为这道题考察的深度很深。这题经常出现在高级或中高级面试中。投资银行更喜欢问这个问题,甚至会要求你实现HashMap来考察你的编程能力。ConcurrentHashMap和其它同步集合的引

我梦黄粱 708

星敏感器:卫星姿态测量的“星空导航仪”

在卫星、航天器和深空探测器的姿态控制系统中,星敏感器(Star Tracker) 是最精确的姿态测量设备之一。它通过识别恒星的位置,计算出航天器在惯性空间中的三轴姿态,精度可达 角秒级(arcsecond),是许多高精度任务(如遥感、天文观测、深空探测)的核心传感器。

ScilogyHunter的博客 3697

把 Claude Code 接进 ABAP 世界,vibing-steampunk 到底能做什么

《Vibing Steampunk:AI Agent 与 ABAP 开发的工程化桥梁》摘要 Vibing Steampunk(vsp)是一个连接 AI Agent 与 SAP ABAP 开发环境的开源工具,通过 Model Context Protocol(MCP)将 ABAP 开发能力标准化。它解决了传统 ABAP 开发中 AI 辅助工具的局限性,使 Claude Code 等 AI 编程助手能够直接访问 ADT API,实现代码读写、调试、测试等完整开发流程。该工具采用 Go 语言开发,提供 147 个

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

(二)Java集合专题-详细分析HashtableJDK1.8集合底层实现的思想

(一)详细分析Hashtable1.8集合底层实现的思路和源码 (1)先来看一下Hashtable的结构图:首先他和HashMap结构都是一样的,都是由数组和链表进行实现的(在JDK1.8是用数组和链表和红黑树的),每一个数组里面存的是一个Entry节点,节点里面有Key、Value,唯一最大 的区别就是HashMap实现不是线程安全的,但是Hashtable是线程安全的,但是Hashtable在...

王伟的博客 2306

深度解读 ABAP CDS View 中的 @Analytics.dataCategory: #FACT 注解

注解在 ABAP CDS View 中具有重要意义。它不仅定义了当前视图在数据模型中的角色,还为后续的分析和报告奠定了基础。在实际开发中,通过合理使用这一注解,可以显著提升数据模型的可用性和系统性能。无论是简单的 KPI 展示还是复杂的多维分析,这一注解都能发挥关键作用。

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

【Java基础】HashMap原理及常见面试题目

1 有1000_000条不同的记录需要插入到HashMap,怎样插入效率更高并说明理由? 这里考察的是capacity 和loadFactor, 当插入记录达到capacity*loadFactor时,需要对HashMap重新分配空间(rebuilt)和拷贝数据。所以对于需要插入很多数据时,比较好的方法是初始容量分配size*2的空间。 import java.util.HashMap; /** * to insert 1000_000 elements into hashmap, test t

freeRoad 1724

微信小程序 - 真机调试调用接口报错{errno: 600001, errMsg: “request:fail -102:net::ERR_CONNECTION_REFUSED},真机运行测试请求异常

微信小程序,真机,调试测试,运行报错,API接口,服务器接口都报错了,小程序报错errno: 600001,小程序报错request:fail -102:net::ERR_CONNECTION_REFUSED,真实手机打开接口全都失效了,微信小程序真机测试失败,微信小程序{errno: 600001, errMsg: "request:fail -102:net::ERR_CONNECTION_REFUSED"},详细解决方法,https,服务器SSL证书,无法解决,服务器更换SSL证书,接口地址改了也不行

🏆 前端领域优质创作者 1万+

【YOLOv11多模态涨点改进】独家创新首发| TGRS 2026 |引入 CIFusion 通道交互融合模块,通过跨特征交互机制强化目标区域响应,适合多模态融合目标检测,小目标检测高效涨点

🔥本文提出了一种改进YOLOv11多模态目标检测模型的方法,通过引入CIFusion通道交互融合模块来提升RGB与红外特征融合效果。CIF模块采用通道级自适应交互机制,根据全局上下文动态分配不同模态通道的重要性,实现跨模态特征交互与互补信息融合。相比传统拼接或加权融合方式,CIF能够增强目标区域响应,抑制背景噪声,提高小目标和低对比度目标的检测性能。实验表明,该方法在DroneVehicle等数据集上显著提升了检测精度。论文还详细介绍了CIF模块的结构原理、实现代码及模型配置方法。

Ai缝合怪、助力小伙伴高效发论文 187

Java经典面试问题:HashMap源码分析,带你搞懂HashMap的工作原理

HashMap作为Java集合框架中一个极其常用的框架,平时我们可能都已经十分熟悉他的用法了。然后面试中经常会被问到其内部的实现原理。本文就带各位来看一下,我们经常打交道的HashMap内部运行机制是怎样的。一、基本介绍HashMap作为集合框架中Map接口下的一个实现类,内部存储的是key-value的键值对形式。对于key的唯一性,HashMap采用如下方式:即先判断两个对象的hashcode...

火凤燎原的博客 1962

华为OD机试真题 新系统 2026-09-26 Java&Go&C【数据中心最佳维护时间窗】

数据中心使用一个整数数组 scores,记录 N 小时内每小时的服务器得分(可正可负)。0 表示服务器故障(无效小时)。

MISAYAONE的博客 99

[java进阶]——HashMap的底层实现原理和源码分析,另附几个高频面试题

🌈键盘敲烂,年薪30万🌈。JDK8以后底层使用 数组+链表+红黑树的数据结构,当链表长度大于8并且数组长度大于64,链表自动转为红黑树。首先明确一个问题: hash是根据key值计算出来的,但是key值不同计算出来的hash值也可能相同,这叫hash碰撞,但是hash值不同key一定不同

Panci_的博客 1319

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Java面试宝典:HashMap底层原理

HashMap 基于数组+链表/红黑树实现,通过哈希函数(扰动高位优化分布)计算键的索引位置,用链表解决哈希冲突(Java 8 后链表超8转红黑树优化查询效率),动态扩容时容量翻倍并重哈希(负载因子默认0.75),非线程安全(多线程推荐 ConcurrentHashMap)。其设计核心是空间换时间,通过高效哈希计算、冲突管理和扩容策略实现 O(1) 平均操作复杂度,需注意键对象的 hashCode() 和 equals() 正确实现以保障性能。

笃行其道的博客 2181

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

14 Java集合(Map集合+HashMap+泛型使用+集合面试题)

Java集合:Map集合、HashMap、泛型使用、集合面试题

yinying293的博客 1522

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

深入剖析HashMap底层实现以及面试题

在 Java 编程中,HashMap 是最常用的数据结构之一,广泛应用于各种场景中。它提供了高效的键值对存储和快速的查找、插入、删除操作。然而,尽管 HashMap 的使用非常简单,但其底层实现却相当复杂,尤其是从 Java 7 到 Java 8 的演变过程中,HashMap 经历了多次重要的优化和改进。

weixin_65696001的博客 669

HashMap的底层结构和实现原理

转自:https://www.cnblogs.com/chengxiao/p/6059914.html   哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,重要性可见一斑。本文会对java集合框架中的对应实现HashMap的实...

C18298182575的博客 1万+

HashMap底层实现原理

HashMap实现原理及源码分析   哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,重要性可见一斑。本文会对java集合框架中的对应实现HashMap的实现原理进行讲解,然后会对JDK7的HashMap源码进行分析。 目录...

qq_40118851的博客 5万+

HashMap底层原理与面试题

文章目录1 类图2 属性2.1 常见属性介绍2.2 底层数据结构2.3 重要常量2.4 链表和树的问题1 为什么不直接采用红黑树?2 为什么采用红黑树,而不是AVL平衡树?3 构造器3.1 常用构造器4 put方法4.1 put操作的整体思路4.2 树操作(略)5 get方法6 哈希处理6.1 如何有效较少碰撞6.2 hash的实现6.2 HashMap底层数组为什么总是2的n次方?7 扩容resize7.1 代码分析7.2 扩容的问题源码分析地址参考资料 1 类图 HashMap是非常常用的工具类,实现M

大唐雨夜的博客 2903

Java面试题:ArrayList底层实现原理、HashMap的实现原理、HashMap的jdk1.7和jdk1.8有什么区别

ArrayList底层实现原理、扩容;ArrayList和LinkedList区别;HashMap的实现原理、HashMap的jdk1.7和jdk1.8有什么区别、HashMap put方法的具体流程,HashMap与Hashtable区别

qq_44700578的博客 1162

c++ map底层实现原理_面试:说一下HashMap的底层实现原理,我懵了

之前我们对Java集合框架一些常见的面试题做了一个总结式的回答:《【29期】Java集合框架 10 连问,你有被问过吗?》这篇开始针对29期中的一些比较重要的问题做详细的解答。哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,重要...

weixin_42347357的博客 2969

Java面试-HashMap 的底层实现原理(JDK 8):数组 + 链表 + 红黑树

HashMap👋 欢迎阅读《Java面试200问》系列博客!🚀大家好,我是Jinkxs,一名热爱Java、深耕技术一线的开发者。在准备和参与了数十场Java面试后,我深知面试不仅是对知识的考察,更是对理解深度与表达能力的综合检验。✨本系列将带你系统梳理Java核心技术中的高频面试题,从源码原理到实际应用,从常见陷阱到大厂真题,每一篇文章都力求深入浅出、图文并茂,帮助你在求职路上少走弯路,稳拿Offer!🔍今天我们要聊的是:《准备好了吗?Let’s go!

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

【JDK源码】HashMap源码分析(附常见面试题)

HashMap源码分析(附面试题) 1.什么是哈希? 在分析HashMap之前,我们先来了解什么是哈希? 概念:Hash也称散列、哈希,对应的英文都是Hash。基本原理就是把任意长度的输入,通过Hash算法变成固定长度的输出这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串就是哈希值。 Hash的特点: 从hash值不可以反向推导出原始的数据 输入数据的微小变化会得到完全不同的hash值,相同的数据会得到相同的 哈希算法的执行效率要高效,长的文本也能快速地计算出哈希值 hash算法的冲.

小皮皮LL的博客 2291

Java集合面试全攻略:底层原理解析与实战技巧

Arrays.asList转换list之后,如果修改了数组的内容,list会受影响,因为它的底层使用的Arrays类中的一个内部类ArrayList来构造的集合,在这个集合的构造器中,把我们传入的这个集合进行了包装而已,最终指向的都是同一个内存地址。Arrays.asList转换list之后,如果修改了数组的内容,list会受影响,因为它的底层使用的Arrays类中的一个内部类ArrayList来构造的集合,在这个集合的构造器中,把我们传入的这个集合进行了包装而已,最终指向的都是同一个内存地址。

m0_68778396的博客 1447

HashMap实现原理及源码分析

HashMap实现原理及源码分析   哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,重要性可见一斑。本文会对java集合框架中的对应实现HashMap的实现原理进行讲解,然后会对JDK7的HashMap源码进行分析。 目录...

北半球先生的博客 1091
上一篇: 【性能优化】两种方案优化双层for循环语句 附代码和实例
下一篇: 【Session】关于在用户退出登录的时候清除相关session信息
少侠露飞
博客等级 码龄9年 214粉丝 117原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值