目录
前言:
在 Java 编程的浩瀚世界中,数据结构的选择和运用至关重要。而 HashMap
作为 Java 集合框架中的一个核心数据结构,在众多应用场景中发挥着举足轻重的作用。
1.创建Map的对象
Map<String, String> m = new HashMap<>();//这里要用MAp的实现类HashMap来创建新的对象
2.添加元素
m.put("郭靖", "黄蓉");
m.put("韦小宝", "沐剑屏");
m.put("尹志平", "小龙女");
String result = m.put("韦小宝", "霜儿");
//此时霜儿就会把沐剑屏给覆盖
//返回的String的值会是被覆盖的沐剑屏,如果说没有被覆盖的值,就会返回null
System.out.println(result);
System.out.println("-------------------");
3.删除元素
String result1 = m.remove("郭靖");
System.out.println(result1);//返回的是他的”值“,也就是黄蓉,如果删除的是黄蓉,返回的是null,对集合中的元素没有影响
System.out.println("--------------remove------------");
4.清空数据
m.clear();
5.判断是否包含
//判断键是否存在
boolean result3 = m.containsKey("郭靖");
System.out.println(result3);
//判断值是否存在
boolean result4 = m.containsValue("小龙女");
System.out.println(result4);
6.获取集合的长度
int Size=m.size();
System.out.println(Size);
System.out.println(m);
7.迭代器
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
String key = it.next();//获取他的键
String s= m.get(key);//获取它的值
System.out.println(key+"="+s);
}
System.out.println("-----------------");
8.增强for
for (String k : keys) {
String s = m.get(k);
System.out.println(k+"="+s);
}
9.Lamdba表达式
map.forEach(( student, s)-> System.out.println(student+" "+s));
10.Enterset方法
Set<Map.Entry<student, String>> entries = map.entrySet();//将集合中的每一个键值对提取出来放到新的set集合中
for (Map.Entry<student, String> entry : entries) {
student key=entry.getKey();
String str1=key.toString();
String str = entry.getValue();
System.out.println(str1+"="+str);
}
11.HashMap的扩容操作是怎么实现的?
不管是JDK1.7或者JDK1.8 当put方法执行的时候,如果table为空,则执行resize()方法扩容。默认长度为16。
11.1 JDK1.7扩容
条件:发生扩容的条件必须同时满足两点
1.当前存储的数量大于等于阈值
2.发生hash碰撞
因为上面这两个条件,所以存在下面这些情况
就是hashmap在存值的时候(默认大小为16,负载因子0.75,阈值12),可能达到最后存满16个值的时候,再存入第17个值才会发生扩容现象,因为前16个值,每个值在底层数组中分别占据一个位置,并没有发生hash碰撞。
当然也有可能存储更多值(超多16个值,最多可以存26个值)都还没有扩容。原理:前11个值全部hash碰撞,存到数组的同一个位置(这时元素个数小于阈值12,不会扩容),后面所有存入的15个值全部分散到数组剩下的15个位置(这时元素个数大于等于阈值,但是每次存入的元素并没有发生hash碰撞,所以不会扩容),前面11+15=26,所以在存入第27个值的时候才同时满足上面两个条件,这时候才会发生扩容现象。
特点:先扩容,再添加(扩容使用的头插法)缺点:头插法会使链表发生反转,多线程环境下可能会死循环
扩容之后对table的调整:
table容量变为2倍,所有的元素下标需要重新计算,newIndex = hash (扰动后) & (newLength - 1)
11.2 JDK1.8扩容
条件:
当前存储的数量大于等于阈值
当某个链表长度>=8,但是数组存储的结点数size() < 64时
特点:先插后判断是否需要扩容(扩容时是尾插法)缺点:多线程下,1.8会有数据覆盖
举例:
线程A:往index插,index此时为空,可以插入,但是此时线程A被挂起
线程B:此时,对index写入数据,A恢复后,就把B数据覆盖了
扩容之后对table的调整:
table容量变为2倍,但是不需要像之前一样计算下标,只需要将hash值和旧数组长度相与即可确定位置。
如果 Node 桶的数据结构是链表会生成 low 和 high 两条链表,是红黑树则生成 low 和 high 两颗红黑树
依靠 (hash & oldCap) == 0 判断 Node 中的每个结点归属于 low 还是 high。
把 low 插入到 新数组中 当前数组下标的位置,把 high 链表插入到 新数组中 [当前数组下标 + 旧数组长度] 的位置
如果生成的 low,high 树中元素个数小于等于6退化成链表再插入到新数组的相应下标的位置
最后:
通过对 Java HashMap
的深入探讨,我们可以清晰地看到它在 Java 编程中的重要性和广泛适用性。从简单的数据存储到复杂的业务逻辑处理,HashMap
都能为我们提供高效、便捷的解决方案。
在不断发展的 Java 生态中,我们应持续深入学习和掌握各种数据结构的特点和用法,以便在实际开发中做出更加明智的选择。希望本博客能为你在使用 Java HashMap
时提供有价值的参考和启发,助力你在 Java 编程的道路上更加自信地前行。期待你在未来的编程之旅中创造出更加精彩的应用。