HashMap源码学习笔记

一、什么是哈希、哈希函数、哈希值、哈希表?

学会HashMap源码,一篇就够了_hashmap 源码-CSDN博客

哈希(Hash)也称散列,是一种算法,用于将任意长度的输入映射为固定长度的输出,这个映射规则就是对应的Hash算法,而映射后的二进制串就是哈希值。

哈希表又称为散列表,通常由一个固定大小的数组和对应的哈希函数组成。当需要插入或查询数据时,首先将关键字(key)通过哈希函数计算得到对应的哈希值(hash value),然后将数据存储在哈希值对应的数组位置上。当需要查询数据时,也是通过哈希函数计算得到哈希值,然后直接访问对应的数组位置,从而快速地找到目标数据。   哈希表的优势在于其快速的插入、删除和查询操作,适用于大量的数据存储和查找场景。然而,哈希表也存在一些问题,例如哈希冲突(多个关键字计算得到相同的哈希值)可能会导致插入和查询操作的效率下降。 

网易面试:JDK1.8将HashMap 头插法 改 尾插法,为何?_hashmap1.8是头插还是尾插-CSDN博客

 

二、HashMap 

HashMap 基于哈希表的 Map 接口实现,主要用来存放键值对(允许使用 null 值和 null 键),是非线程安全的。 

Java8中链表长度超过8时会把长度超过8的链表转化成红黑树。

三、源码分析

1、属性值:

  //默认table大小(为2的n次幂)这样可以减少碰撞,让存取更高效
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
 
    //默认table最大长度
    static final int MAXIMUM_CAPACITY = 1 << 30;
 
    //默认负载因子大小
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
 
    //树化阈值(当某个桶中的链表长度大于该值时,有可能转换成红黑树)
    static final int TREEIFY_THRESHOLD = 8;
 
    //树降级成为链表的阈值
    static final int UNTREEIFY_THRESHOLD = 6;
 
    //树化阈值(当哈希表中的所有元素个数超过64时,才会允许树化)
    //即树化条件TREEIFY_THRESHOLD和 MIN_TREEIFY_CAPACITY 都满足时才会树化
    static final int MIN_TREEIFY_CAPACITY = 64; 

2、在构造方法中调用其他构造方法

3、四个构造方法

/**
     * 构造一个具有指定初始容量和负载因子的空 HashMap。
     *
     * @param initialCapacity 初始容量
     * @param loadFactor      负载因子
     * @throws IllegalArgumentException 如果初始容量为负数或加载因子为非正数
     */
    public My_HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
        this.loadFactor = loadFactor;
        // tableSizeFor(cap) 返回大于且最接近 cap 的 2^n,例如 cap = 17 时,返回 32
        this.threshold = tableSizeFor(initialCapacity);
    }

    /**
     * 构造一个具有指定初始容量和默认负载因子 (0.75) 的空 HashMap。
     *
     * @param initialCapacity 初始容量
     * @throws IllegalArgumentException 如果初始容量为负数
     */
    public My_HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。
     */
    public My_HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // 其他字段默认值
    }

    /**
     * 构造一个具有与指定 Map 相同映射关系的新 HashMap。
     * HashMap 使用默认加载因子 (0.75) 和足以容纳指定 Map 映射关系的初始容量。
     *
     * @param m 要放入此映射中的映射
     * @throws NullPointerException 如果指定的映射为 null
     */
    public My_HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }

4、hashcode方法

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

5、增强型for循环

//判断哈希表中是否包含指定的值。

    public boolean containsValue(V value) {
        for (Map.Entry<K, V> entry : entrySet()) {  // 遍历所有键值对
            if (entry.getValue().equals(value)) {  // 如果找到相同的值则返回true
                return true;
            }
        }
        return false;  // 没有找到返回false
    }

entrySet() 方法返回一个包含Map中所有键值对的Set集合。

for (Map.Entry<K, V> entry : entrySet())

这句话的意思:遍历一个 Map 中所有的键值对(entry)

6、 BiFunction<K, V, V> 函数 

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}

BiFunction 是 Java 中的一个函数式接口(Functional Interface),用于表示接受两个参数并返回结果的函数。具体来说,BiFunction 接口定义了一个函数方法 apply,该方法接受两个参数,分别是参数类型为 TU 的对象,返回一个结果类型为 R 的对象。 

使用举例:

//使用给定的 BiFunction<K, V, V> 函数来替换 Map 中每个键值对的值。

    public void replaceAll(BiFunction<K, V, V> function) {
        for (Map.Entry<K, V> entry : entrySet()) {  // 遍历所有键值对
            K key = entry.getKey();  // 获取键
            V value = entry.getValue();  // 获取值
            V newValue = function.apply(key, value);  // 应用函数计算新值
            put(key, newValue);// 替换键值对的值
        }
    }
// 测试 forEach() 方法
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("cherry", 3);
        map.forEach((key, value) -> System.out.println(key + ": " + value));
        System.out.println("---after putIfAbsent:");


        //测试putIfAbsent
        map.putIfAbsent("lixin", 1);
        map.forEach((key, value) -> System.out.println(key + ": " + value));
        System.out.println("---after replaceAll:");


        //测试replaceAll函数和compute函数
        BiFunction<String, Integer, Integer> doubleFunction = (a, b) -> b * 2;
        // 使用 replaceAll 方法替换所有值为原值的两倍
        map.replaceAll(doubleFunction);
        map.forEach((key, value) -> System.out.println(key + ": " + value));
        System.out.println("---after compute:");

        map.compute("lixin", doubleFunction);

        map.forEach((key, value) -> System.out.println(key + ": " + value));

 输出:

  • 30
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值