一、什么是哈希、哈希函数、哈希值、哈希表?
学会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
,该方法接受两个参数,分别是参数类型为T
和U
的对象,返回一个结果类型为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));
输出: