目录
一、特点
无序,键值对,键不能重复,值可以重复,键重复则覆盖,没有继承Collection接口
二、扩容
扩容:初始容量16,负载因子0.75,扩容增量1倍
三、遍历
准备数据
private Map<String, Object>map=new HashMap<String, Object>();
@Before
public void setup() {
map.put("1", "鼠标");
map.put("1", "电脑");
map.put("2", "风扇");
map.put("3", "书包");
map.put("4", "演讲");
map.putIfAbsent("1", "aaa");//如果缺失则加进去
}
🟡先获取所有键的Set集合,再遍历(通过键获取值)
@Test
public void test01() {
Iterator<String>it=map.keySet().iterator();
while(it.hasNext()) {
String key = it.next();
System.out.println(map.get(key));
}
}
效果图如下:
🟡取出保存所有Entry的Set,再遍历此Set即可
@Test
public void test02() {
Iterator<Entry<String, Object>> it = map.entrySet().iterator();
while(it.hasNext()) {
Entry e=it.next();
System.out.println("key = "+ e.getKey() +" value ="+ e.getValue());
}
}
效果图如下:
四、实现
-
HashMap
HashMap的线程不安全,但是最为常用,速度快。内部采用数组来存放数据。
首先来了解了解执行过程
当我们往map里面put 元素时,会先检查这个数组是否存在。若没有存在,则会分配一个默认长度为16的数组。数组里面不是直接保存元素,而是经过一系列运算,通过给出的key获取到key在数组中的位置,这个数组称为桶(通过计算算出节点在几号桶里面)。图中桶指向的元素是Node节点(Node实际是Entry<k,v>)。每次添加元素时会经过计算,若没有元素,则会声明一个节点放进去,如果存放的元素是一样的则会被覆盖,反之,则会通过链表的方式往下增加(新增得元素在上方,原来的元素往下走)。
如何拿值?先经过计算,通过key找到元素所在的桶,若发现是一个链表,则进行链表遍历的方式找到元素。
在元素多的情况下,上面的方法查找的时候需要全部遍历,存在缺点。运用二叉树的方式进行查找 ,通过元素的条件进行遍历,不需要那个链表都进行遍历。
了解结构之后再来看看基本原理
首先向Map里面put值,若发现为空,生成一个新的空,再根据key的hush值计算数组中对应的下标。如果数组里面没有值则生成一个新的节点,有值得话就生成节点进行判断,若大于8,转换为红黑树保存节点信息,不大于8就将信息保存到链表中。
注意:流程图中绿色标出的部分为JDK8新增的处理逻辑,目的是在Table[i]中的Node节点数量大于8时,通过红黑树提升查找速度。
-
HashTable
HashTable线程安全但是不为常用
public void test03() {
Map<Integer, Object>table=new Hashtable<Integer, Object>();
table.put(1, "张三");
table.put(2, "李四");
table.put(3, "王五");
table.put(4, "老刘");
Iterator<Integer> it = table.keySet().iterator();
while(it.hasNext()) {
int key=it.next();
System.out.println(table.get(key));
}
}
-
ConcurrentHashMap
线程安全,比HashTable性能高
public void test04() {
Map<Integer, Object>cmap=new ConcurrentHashMap<Integer, Object>();
cmap.put(1, "张三");
cmap.put(2, "李四");
cmap.put(3, "王五");
cmap.put(4, "老刘");
Iterator<Integer> it = cmap.keySet().iterator();
while(it.hasNext()) {
int key=it.next();
System.out.println(cmap.get(key));
}
}
-
TreeMap
key值按一定的顺序排序,添加或获取元素时性能较HashMap慢,是因为需求维护内部的红黑树,用于保证key值的顺序。
public void test05() {
Map<Integer, Object>tmap=new TreeMap<Integer, Object>();
tmap.put(1, "张三");
tmap.put(2, "李四");
tmap.put(3, "王五");
tmap.put(4, "老刘");
Iterator<Integer> it = tmap.keySet().iterator();
while(it.hasNext()) {
int key=it.next();
System.out.println(tmap.get(key));
}
}
key值默认为升序,若想按照降序排序可以使用比较器(用key进行比较)
public void test05() {
Map<Integer, Object>tmap=new TreeMap<Integer, Object>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
tmap.put(1, "张三");
tmap.put(2, "李四");
tmap.put(3, "王五");
tmap.put(4, "老刘");
Iterator<Integer> it = tmap.keySet().iterator();
while(it.hasNext()) {
int key=it.next();
System.out.println(tmap.get(key));
}
}
效果图如下:
-
LinkedHashMap
继承HashMap,LinkedHashMap是有序的,且默认为插入顺序,当我们希望有顺序地去存储key-value时,就需要使用LinkedHashMap了
public void test06() {
Map<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("name1", "josan1");
linkedHashMap.put("name2", "josan2");
linkedHashMap.put("name3", "josan3");
Set<Entry<String, String>> set = linkedHashMap.entrySet();
Iterator<Entry<String, String>> iterator = set.iterator();
while(iterator.hasNext()) {
Entry entry = iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println("key:" + key + ",value:" + value);
}
}
效果如下: