目录
当我们new HashMap()时HashMap数组的长度就被创建了吗?
Map-HashMap:
hashMap概述:
JDK1.8之前是由数组加链表进行储存,1.8之后加入了红黑树,数组长度超过64链表长度超过8就会进行转红黑树进而提高效率,当长度少于8是又会转为链表。
Map几个主要实现类对比:
HashMap:因为没有实现锁机制,所以它是线程不安全的,同时也是无序的,key只允许有一个为null。
TreeMap: 能够把它保存的记录根据key排序,默认是按升序排序,无序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。也是线程不安全的。
Hashtable: 与 HashMap类似,不同的是,key和value的值均不允许为null,实现了锁机制所以是线程安全的,所以效率慢,hashtable是继承Dictionary抽象类,hashMap和treeMap都继承自AbstractMap抽象类,LinkedHashMap继承自hashMap。
LinkedHashMap: 保存了记录的插入顺序,有序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,线程不安全。
什么是哈希冲突
当我们计算hash值得到的储存地址,上面已经被其他元素占有了,这就是哈希冲突,也叫哈希碰撞
当我们new HashMap()时HashMap数组的长度就被创建了吗?
上图可以看到当我们new一个HashMap时,如果你没有指定加载因子,他的构造函数就只做了一件事就是,指定了默认的加载因子(默认0.75)。
HashMap什么时候数组长度才被创建?
当HashMap第一次调用put方法时的时候HashMap才会被创建。
HashMap的put执行过程
当我们put一个键值的时候:
- 首先他会调用一个hash()计算键的一个hash值。
- 调用putVal,首先会判断当前,Node [] table数组是否是空的,如果是空或者长度为0,就需要调用resize()进行table数组的创建。
- resize(),通过默认的加载因子(默认0.75)和初始化容量(默认16)计算出table的数组长度,并且创建数组table
-
根据key的hash值通过&字符计算出需要存放在table数组的下标位置在
-
如果当前table数组的下标识空的,就创建一个新的链表添加进去(HashMap链表是单链表),最后判断长度是否大于加载因子是否触发数组table的扩容resize()
-
如果单前下标存在链表且,hash值值和key的值都相等,就进行一个重新赋值,改变value。
-
当hash值和key不相等时,就会进行判断,判断该下标是否是一个,树结构,进行树的一个put,树的添加还没仔细看(跳过,后面补充)
-
以前条件都不成立,就进行当前下标的链表通过for循环进行一个查找。当找到位置为null的时候添加一个新的链表,判断是否触发链表转为树结构操作(默认大于8进行转换树方法,方法中判断表长度是否大于64(默认64),大于转树,小于进行数组的扩容。如果找到相同的hash值和键相同就重新赋值操作。
遍历HashMap效率最高的entrySet()
for (Map.Entry entry : hashMap.entrySet()){
System.out.println("key:" + entry.getKey() + " " + "value:" + entry.getValue());
}