不应该使用数组作为HashMap的key
原因分析:
HashMap有比较两个key是否相同的需要,并且hashMap是通过hashCode来判断key是否相同的.
数组的hashCode:数组的hashCode是以其地址作为依据,而并非数组的内容作为依据.
这就导致及时两个数组的内容是一样的,但是因为其地址不同,所以其hashCode还是不同,因此当两个相同的数组放入到hashMap中,不会覆盖,而是放入两个"不同"的key-value对象.
“不同”:指hashMap认为他们是不同的.
综上所述,使用数组作为HashMap的key不能得到我们想要的结果.
让我们看看代码:
import java.util.HashMap;
public class Main {
public static void main(String[] args){
int[] array1={1,2,3,4};
int[] array2={1,2,3,4};
HashMap<int[],String> map = new HashMap<>();
System.out.println("the hashmap's size "+map.size());
map.put(array1,"this is the array1");
System.out.println("after added array1,the hashmap's size "+map.size());
map.put(array2,"this is the array2");
System.out.println("after added array2,the hashmap's size "+map.size());
}
}
我们可以看到输出,即尽管array1和array2中的元素都一样,但是hashmap还是将它们看作是同一个key,因此添加了两个.
解决方案:
但是我们还是希望对这个数组的对象使用HashMap的功能.
-
将数组转化为String作为key
-
使用List代替数组作为key
关于hashMap的其他注意点:
- 最好不要用Object作为HashMap的key
- 如果要使用,需要覆写equals和hashCode方法,并适当简单
- HashMap是线程不安全的,java提供了线程安全的版本ConcurrentHashMap
HashMap的底层实现:
- jdk7中使用数组加链表
- jdk8中使用数组加链表/红黑树(防止一个桶里的链表过于长,导致效率低下,加入了红黑树的存储方式)
主要思路:
1. 基本节点数据结构Node
HashMap中每一个节点使用的是Node类,包含了key、value、hash、next。
2. 数组大小问题
其数组默认大小是16,但是当用户初始化指定capacity时,(注意:capacity并非map的极限容量,只是决定你要把这个数组开多大,因此你put了多余capacity的元素也不会出现问题, 数组大小为大于等于capacity的最小2的整数幂。
3. 数组扩大问题
3.1 发生条件:
- 当size>thresHold,即node总数大于数组大小时就进行扩容。(node几乎不会各占一个位置,很大可能集中到几个数组元素中了)
- 当某条链表大小>8,但是数组大小还小于64;优先进行扩容而不是转化红黑树
3.2 扩容方式
capacity变为两倍(不超过最大限制2^30的话),对于每个元素进行rehash重新放入.
4. 链表转红黑树的条件
当一个数组的node对应的链表的大小超过TREEIFY_THRESHOLD(常量8)时,
- 如果数组大小大于MIN_TREEIFY_CAPACITY(常量64),会转化为红黑树。
- 如果数组大小没有超过64,优先进行resize()
5. 红黑树转链表的条件
主要根据树具体结构,官方注释是2~6个node的时候
Node
hashmap中每个节点是一个node,成员变量包括key,value,hash,next;并不是简单的键值对;Node类的一些方法:
- toString(): key+“=”+value
- hashCode():hashCode(key)^hashCode(value)
- setValue(newValue):新value替换原value,返回原value
- 键和值都相等.
hashMap中的成员变量:
- Node<K,V> [] table:桶数组
- size:map中键值对的数量
- modCount:记录该map结构修改的次数,包括增加删除/改变结构/扩容等,
- threshold:table array的大小(capacity*loadFactor)
- loadFactor:table的负载因子