hashMap底层原理

            之前去大华面试,问道了hashMap底层的一些东西,对于我这种毕业一年不到的人来说,就输的很惨,所以一些底层的,基础的还是要加强呀!

            说一下这个HashMap,有些人可能只会知道Map接口的实现类,比如hashMap,hashTable,treeMap的差别。这里简单的讲一下,hashTable、hashMap的实现原理都是一样的,都是基于哈希表实现的,区别就是hashTable是线程同步,hashMap不是。而treeMap是基于红黑树的数据结构实现的,实现key排序。

         然后回到主题,hashMap是数组,链表来实现对数据的存储,但这两者基本上是两个极端,就是这两个都是各自为了实现某种特定的功能实现的。

下面简单的讲一下这两种数据结构的特点与缺点

数组

数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难

链表

链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。


而我们的哈希表就是综合两种数据结构的

这里写图片描述


hasMap的构造函数

public HashMap(int initialCapacity, float loadFactor) {
        //初始容量不能<0
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: "
                    + initialCapacity);
        //初始容量不能 > 最大容量值,HashMap的最大容量值为2^30
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //负载因子不能 < 0
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: "
                    + loadFactor);

        // 计算出大于 initialCapacity 的最小的 2 的 n 次方值。
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        
        this.loadFactor = loadFactor;
        //设置HashMap的容量极限,当HashMap的容量达到该极限时就会进行扩容操作
        threshold = (int) (capacity * loadFactor);
        //初始化table数组
        table = new Entry[capacity];
        init();
    }

  从源码中可以看出,每次新建一个HashMap时,都会初始化一个table数组。table数组的元素为Entry节点。

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        .......
    }



通俗理解:

这里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这个属性链接在一起。所以疑问不用担心。也就是说数组中存储的是最后插入的元素。到这里为止,HashMap的大致实现,

我们应该已经清楚了。相当于用key的hash值使用数组的形式排列,方便寻找,数组的每个元素又是一个链表,方便进行插入,删除。


接下来附上一个网址,详细解释hashMap在初始化的时候,各个参数的含义

http://blog.youkuaiyun.com/fan2012huan/article/details/51087722



### HashMap 底层数据结构 HashMap底层结构采用 **数组 + 链表/红黑树** 的方式实现。这种结构结合了数组的快速访问特性和链表/红黑树在冲突处理中的高效性。HashMap 通过哈希函数将键(key)转换为哈希值,然后根据哈希值计算出键值对在数组中的存储位置(索引)[^2]。 - **数组**:用于存储数据的主结构,数组的每个元素被称为“桶”(bucket)。 - **链表**:当多个键值对被映射到同一个桶时,使用链表来存储这些冲突的键值对。 - **红黑树**:当某个桶中的链表长度超过一定阈值(默认为8),链表会转换为红黑树以提升查找效率[^2]。 ### HashMap 的实现原理 HashMap 的核心实现原理围绕 **哈希算法** 和 **冲突解决机制** 展开: 1. **哈希算法**:HashMap 使用键的 `hashCode()` 方法结合扰动函数生成最终的哈希值。通过 `(n - 1) & hash` 计算出键值对的存储位置,其中 `n` 是数组的长度。这种方式确保哈希值均匀分布,减少碰撞概率[^5]。 2. **冲突解决**:当两个不同的键经过哈希计算后映射到相同的桶时,会发生哈希冲突。HashMap 使用 **拉链法** 解决冲突,即在每个桶中维护一个链表,存储所有冲突的键值对。当链表长度超过阈值时,链表会转换为红黑树以提升性能。 3. **动态扩容**:当 HashMap 中的元素数量接近其容量与负载因子(默认为0.75)的乘积时,HashMap 会自动扩容(通常是当前容量的两倍),并重新分配所有键值对到新的桶中。这个过程称为 **再哈希**(rehash)[^5]。 4. **线程安全性**:HashMap 不是线程安全的,在多线程环境下可能会导致数据不一致或死循环等问题。如果需要线程安全的实现,可以使用 `ConcurrentHashMap`[^3]。 ### 示例代码:HashMap 的基本使用 ```java import java.util.HashMap; public class HashMapExample { public static void main(String[] args) { // 创建一个 HashMap 实例 HashMap<String, Integer> map = new HashMap<>(); // 添加键值对 map.put("Apple", 10); map.put("Banana", 20); map.put("Orange", 30); // 获取值 System.out.println("Apple: " + map.get("Apple")); // 输出 Apple: 10 // 遍历 HashMap for (String key : map.keySet()) { System.out.println(key + ": " + map.get(key)); } // 删除键值对 map.remove("Banana"); // 检查是否包含某个键 if (map.containsKey("Orange")) { System.out.println("Contains Orange"); } } } ``` ### 性能优化与使用场景 HashMap 的性能优势主要体现在 **快速的插入、查找和删除操作**,其平均时间复杂度为 O(1)。这种高效性得益于哈希算法的均匀分布和冲突处理机制的优化(链表转红黑树)。 在实际开发中,HashMap 被广泛用于缓存、索引、快速查找等场景。例如,在金融领域中,HashMap 和 `ConcurrentHashMap` 被频繁用于处理高并发的数据存储和访问需求[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值