Java基础之:集合——Map——HashMap
HashMap简单介绍
-
Map接口的常用实现类:HashMap、Hashtable和Properties。
-
HashMap是 Map 接口使用频率最高的实现类。
-
HashMap 是以 key-val 对的方式来存储数据 [案例 Entry ]
-
key 不能重复,但是是值可以重复,允许使用null键和null值。
-
如果添加相同的key , 则会覆盖原来的key-val ,等同于修改.(key不会替换,val会替换)
-
与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的.
-
HashMap没有实现同步,因此是线程不安全的,文档 The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.)
关于HashMap的案例,就是上一篇博客中描述Map的案例。使用的是HashMap。
HashMap底层机制
(k,v)是一个Node节点,实现了Map.Entry<K,V>。
jdk7.0的hashmap 底层实现[数组+链表], jdk8.0 底层[数组+ 链表/红黑树]
这里的链表都只有next属性,没有prev属性,即单向链表。
扩容机制
table表的扩容机制(即上图中第一行类似数组的表):
-
HashMap底层维护了Node类型的数组table,默认为null
-
当创建对象时,将加载因子(loadfactor)初始化为0.75
-
当添加元素时(调用putVal方法),需要通过该元素的哈希值获取在table中的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,需要继续判断是否相等,如果相等则直接覆盖,如果不相等则需要判断此时是树结构还是链表结构,做相应处理。如果添加时发现容量不够就需要扩容。
-
如果第一次添加,则需扩容table容量为16,临界值(threshold)为12.(16 * 0.75 = 12)
-
即当table表的内容增长到临界值(threshold)时,就会自动扩容。(添加第13个元素进入table表时,扩容)
-
如果其他次添加则需扩容table容量到原来的2倍,临界值也改为原来的两倍。(例:第2次扩容,table -> 32 , threshold -> 24)
table表中单个索引位置的扩容机制(链表/红黑树):
-
在table表中,一开始默认是长度为16的数组
-
当某一个索引中的元素超过了8时,它优先会选择将长度扩容2倍(即长度为32)的数组。
-
它会认为是不是由于数组的长度不够才导致一个索引中元素过多。
-
但当它发现长度为32时,某一个索引中的元素还是超过9时,它还是会优先选择将长度再次扩容到2倍(即长度为64)的数组。
-
在长度为64的数组中,它发现某一个索引中的元素还是超过10时,它就会对该索引所在的链表转化为红黑二叉树.
-
所以在第十一个元素时,才会对该索引所在的链表转化为红黑二叉树.
测试代码
import java.util.HashMap; public class HashMapTreeNode { @SuppressWarnings({ "rawtypes", "unchecked" }) public static void main(String[] args) { HashMap map = new HashMap(); for (int i = 1; i <= 20; i++) { User user = new User(); map.put(user, user); System.out.println("i =" + i); } } } class User { // 保证每个元素放入同一个索引,将hashcode设置为1 @Override public int hashCode() { return 1; } // 保证在同一个索引中,每个元素不会因为相同被覆盖,将equles设置为false @Override public boolean equals(Object obj) { return false; } }
LinkedHashMap
LinkedHashMap继承自HashMap,包含了HashMap的所有机制,但不同的是LinkedHashMap是有序的(即添加和取出顺序相同).
简单案例:
package class_HashMap; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class ClassTest01_LinkedHashMap { @SuppressWarnings({ "unchecked", "rawtypes" }) public static void main(String[] args) { HashMap map = new LinkedHashMap(); map.put("1", "hello01"); map.put("2", "hello02"); map.put("3", "hello03"); map.put("3", "hello05"); map.put("4", "hello02"); map.put(null, "hello02"); map.put("5", null); //遍历键值对 Set entrySet = map.entrySet(); Iterator iterator = entrySet.iterator(); while(iterator.hasNext()) { Object obj = iterator.next(); Map.Entry node = (Map.Entry)obj; System.out.println(node.getKey() + " = " + node.getValue()); } iterator = entrySet.iterator(); } }
程序输出:
1 = hello01
2 = hello02
3 = hello05
4 = hello02
null = hello02
5 = null