特点:
①HashMap是Map里面的一个实现类。
②没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
③特点都是由键决定的:无序、不重复、无索引
④HashMap跟HashSet底层原理是一模一样的,从名字可以看出来,都是哈希表结构
底层原理:
jdk8 及以后的 HashMap 的底层原理。
- 创建 HashMap 集合后,会在底层创建一个长度为 16 的数组 table,并加载 负载因子为0.75 的 HashMap
- 这意味着当 HashMap 中的元素数量达到数组长度的 75% 时,数组会进行扩容(原有长度*2)操作,以保持较低的碰撞率和良好的性能。
- 为什么加载因子是0.75?【面试题】:这个值是根据空间和时间,通过泊松分布算法得到的一个折中的值。
- **根据元素的键计算哈希值(和值无关)**跟数组的长度计算出应存入的位置
int index=( 数组长度-1 ) & 哈希值
所以说第一个元素存入的位置不一定是 0 索引处,如下
获取元素时就从左到右遍历,所以说 HashMap 是无序的
- 判断当前位置是否为 null,如果是 null 直接存入
- 如果不是 null,表示有元素,这时则调用 equals 方法比较 键
注意这里和 HashSet 不太一样
1. **如果键值是一样的,那么就会覆盖原有的Entry对象,不一样则存入,形成链表**
1. 注意: jdk8 以前:新元素存入数组,老元素挂在新元素下面,jdk8及以后:新元素直接挂在老元素下面。如图:
2. ![image.png](https://cdn.nlark.com/yuque/0/2024/png/40571611/1706352453975-788390f9-db34-4e6e-a341-6ca13d7e95da.png#averageHue=%23f5f4f4&clientId=u5ed0c2fb-24ca-4&from=paste&height=251&id=u205b9bc9&originHeight=377&originWidth=284&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=25649&status=done&style=shadow&taskId=u5d0fab22-0e25-4769-a0a2-69fd04fdb17&title=&width=189.33333333333334)
2. 注意:当链表长度大于 8 **并且** 数组长度 >= 64 时,链表会变成红黑树。如图:
3. ![image.png](https://cdn.nlark.com/yuque/0/2024/png/40571611/1706352648228-7a43a3dd-f57d-4ce8-bd1d-40be46bc18b4.png#averageHue=%23f5efed&clientId=u5ed0c2fb-24ca-4&from=paste&height=378&id=ua663b5fc&originHeight=567&originWidth=1244&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=152634&status=done&style=shadow&taskId=udc3c2a2c-409d-486a-882d-70c76bed134&title=&width=829.3333333333334)
- 重写 hashCode 是为了通过键计算哈希值,而不是地址值
- 重写 equals 是为了比较对象内部属性,
总结:
1.Hash Map底层是哈希表结构的
2.依赖hash Code方法和equals方法保证键的唯一
3.如果键存储的是自定义对象,需要重写hash code和equals方法
如果值存储自定义对象,不需要重写 hash code和equals方法
HashMap 练习 1:存储自定义对象
Student
package com.lt.arr.map.hashmap;
import java.util.Objects;
public class Student {
private String name;
private int age;
// 构造+set+get+toString
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
测试类
public class Test01 {
public static void main(String[] args) {
//创建HashMap集合对象
HashMap<Student, String> m = new HashMap<>();
//添加键值对
m.put(new Student("zhangsan", 23), "jiangxi");
m.put(new Student("lisi", 24), "beijing");
m.put(new Student("zhangsan", 23), "shanghai");//键重复,覆盖
//遍历
//keySet
Set<Student> stu = m.keySet();
for (Student student : stu) {
System.out.println(student + " -" + m.get(student));
}
//EntrySet
Set<Map.Entry<Student, String>> set = m.entrySet();
for (Map.Entry<Student, String> Entry : set) {
System.out.println(Entry.getKey()+"- " + Entry.getValue());
}
//Lambda表达式
m.forEach((Student ,String) -> System.out.println(Student+"- "+String));
}
}
Student{name = lisi, age = 24} -beijing
Student{name = zhangsan, age = 23} -shanghai
若未重写 hashCode 和 equals 方法计算哈希值时
HashMap 练习 2:利用 Map 集合进行统计:
package com.lt.arr.map.hashmap;
import java.util.*;
public class Test02 {
public static void main(String[] args) {
//80名同学用for循环模拟、投票可以用随机数实现、
Random r=new Random();
//景点用数组存储;
String[]Attractions={"A","B","C","D"};
//投票结果用集合存储
ArrayList<String>list=new ArrayList<>();
//循环80次 模拟投票场景
for (int i = 0; i < 80; i++) {
int index = r.nextInt(Attractions.length);//0-3
list.add(Attractions[index]);
}
//如果要统计的东西比较多,不方便使用计数器思想
//用HashMap存储对应关系,键是景区,值是票数,谁多谁少显而易见
HashMap<String,Integer>map=new HashMap<>();
//遍历list集合
for (String attractions : list) {
if (map.containsKey(attractions)){
//若map内存在当前景点,说明已经有人投票了,只需拿出次数+1,再放入即可
Integer count = map.get(attractions);
count++;
map.put(attractions,count);
}else {
//map内无该景点时
map.put(attractions,1);
}
}
//entrySet遍历集合
Set <Map.Entry<String ,Integer>> set= map.entrySet();
for (Map.Entry<String, Integer> entry : set) {
System.out.println(entry.getKey()+" "+entry.getValue());
}
//获取最大的值;
int max=0;
for (Map.Entry<String, Integer> entry : set) {
if (entry.getValue()>max){
max= entry.getValue();
}
}
System.out.println(max);
//将最大值的键打印出来
for (Map.Entry<String, Integer> entry : set) {
if (entry.getValue()==max){
System.out.println(entry.getKey()+"景点想去的人最多"+"有"+max+"人");
}
}
}
}
A 20
B 26
C 18
D 16
26
B景点想去的人最多有26人