HashMap的原理与实现
- 为什么使用数组+链表
- 用LinkedList代替数组可以吗,既然是可以的,为什么不用反而用数组。 数组,也可以称为桶,通过连续的存储单元存储,需要指定下标存储,时间复杂度o(n) 线性链表,新增插入修改o(1),查找 时间复杂度o(n) 红黑树:
重要变量介绍:
ps:重要的变量
DEFAULT_initial_CAPACITYTable数组的初始化长度:1 << 4``2^4=16(为什么要是 2的n次方?- 简化求余操作; 减少扩容时元素移动的概率,提高resize操作的性能。)
数组的初始长度就是默认的长度,是一个静态常量DEFAULT_INITIAL_CAPACITY = 16;
```
对于 i << n,计算方式是 i * (2^n) 1<<4 = 1*(2^4)
对于 i >> n,计算方式是 i / (2^n)
-
maxImum_CAPACITYTable数组的最大长度: 1<<30 1*2^30=1073741824` -
DEFAULT_load_factor负载因子:默认值为0.75。 当元素的总个数>当前数组的长度 * 负载因子。数组会进行扩容,扩容为原来的两倍(todo:为什么是两倍?) -
treeIFY_threShold链表树 阙值: 默认值为8。 表示在一个node(Table)节点下的值的个数大于8时候,会将链表转换成为红黑树。 -
UNTREEIFY_threShold红黑树链 阙值: 默认值为6。 表示在进行扩容期间,单个Node节点下的红黑树节点的个数小于6时候,会将红黑树转化成为链表。 -
MIN_treeify_CAPACITY = 64最小树化阈值,当Table所有元素超过该值,才会进行树化(为了防止前期阶段频繁扩容和树化过程冲突)。
实现原理: 在HashMap中,采用 数组+链表 的方式来实现对数据的储存。
源码分析
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public 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;
this.threshold = tableSizeFor(initialCapacity);
}
/**
返回一个大于n且是最近的2的整次幂的数
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
其他FQA List 转 Map
List转为Map<Object, List<Object>>
Map<Integer, List<String>> ans = new HashMap<>();
for(String str: list) {
List<String> sub = ans.get(str.length());
if(sub == null) {
sub = new ArrayList<>();
ans.put(str.length(), sub);
}
sub.add(str);
}
jdk8+, for循环中的内容可以利用Map.computeIfAbsent来替换
Map<Integer, List<String>> ans = new HashMap<>();
for (String str : list) {
ans.computeIfAbsent(str.length(), v -> new ArrayList<>()).add(str);
}
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
jdk8+ 的写法
public static <K, V> Map<K, List<V>> toMapList(List<V> list, Function<V, K> func) {
return list.stream().collect(Collectors.groupingBy(func));
}
jdk < 1.8的写法
public static <K, V> Map<K, List<V>> toMapList(List<V> list, KeyFunc<V, K> keyFunc) {
Map<K, List<V>> result = new HashMap<>();
for (V item : list) {
K key = keyFunc.getKey(item);
if (!result.containsKey(key)) {
result.put(key, new ArrayList<>());
}
result.get(key).add(item);
}
return result;
}
public static interface KeyFunc<T, K> {
K getKey(T t);
}
Map<Integer, List<String>> res = toMapList(list, new KeyFunc<String, Integer>() {
@Override
public Integer getKey(String s) {
return s.length();
}
});
Guava
Map<Long, User> maps = Maps.uniqueIndex(userList, new Function<User, Long>() {
@Override
public Long apply(User user) {
return user.getId();
}
});