HashMap
说明
HashMap是Map里面的一个实现类。
没有额外需要学习的特有方法,直接使用继承自Map里面的方法就可以了。
特点
特点都是由键决定的:无序、不重复、无索引
HashMap的底层原理
HashMap跟HashSet底层原理是一模一样的,都是哈希表结构
利用键计算哈希值,和值无关
集合中存入的元素时会先将键和值封装为键值对(Entry)类,再将Entry类存入集合
注意事项
HashMap底层依赖hashCode方法和equals方法保证键的唯一
如果键存储的是自定义对象,需要重写hashCode和equals方法
如果值存储自定义对象,不需要重写hashCode和equals方法
案例练习
存储学生对象并遍历
创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。
存储三个键值对元素,并遍历
要求:同姓名,同年龄认为是同一个学生
Student类
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//键是自定义对象,需要重写equals和hashCode方法
//才能通过比较自定义对象的属性值判断是否唯一
@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);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
import java.util.HashMap;
public class Test {
public static void main(String[] args) {
HashMap<Student, String> hm = new HashMap<>();
Student s1 = new Student("zhangsan", 23);
Student s2 = new Student("lisi", 24);
Student s3 = new Student("wangwu", 25);
hm.put(s1, "北京");
hm.put(s2, "上海");
hm.put(s3, "深圳");
hm.forEach((student, s) -> System.out.println(student + " = " + s));
//Student{name='wangwu', age=25} = 深圳
//Student{name='lisi', age=24} = 上海
//Student{name='zhangsan', age=23} = 北京
//s4的键与s1重复,覆盖s1的值
Student s4 = new Student("zhangsan", 23);
hm.put(s4, "重庆");
hm.forEach((student, s) -> System.out.println(student + " = " + s));
//Student{name='wangwu', age=25} = 深圳
//Student{name='lisi', age=24} = 上海
//Student{name='zhangsan', age=23} = 重庆
}
}
统计投票人数
某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.Set;
public class Test1 {
public static void main(String[] args) {
//定义数组记录选项
char[] ch = {'A', 'B', 'C', 'D'};
//定义集合记录投票结果
ArrayList<Character> list = new ArrayList<>();
//模拟投票,将结果存入集合
Random r = new Random();
for (int i = 0; i < 80; i++) {
int index = r.nextInt(ch.length);
char c = ch[index];
list.add(c);
}
//定义双列集合,键是选项,值是该选项的被投次数
HashMap<Character, Integer> hm = new HashMap<>();
//遍历集合list中的每一个选项,判断集合hm的键是否存在该选项
//如果不存在就将该选项存入hm为键,值为1
//如果存在就将该选项对应的值加1
for (char c : list) {
if (hm.containsKey(c)) {
//存在
int count = hm.get(c);
count++;
hm.put(c, count);
} else {
//不存在
hm.put(c, 1);
}
}
//得到最高票数
int max = 0;
Set<Character> chs = hm.keySet();
for (char c : chs) {
int count = hm.get(c);
if (count > max) {
max = count;
}
}
System.out.println(hm);//{A=23, B=14, C=15, D=28}
//判断哪一个选项对应的票数是最高票
for (char c : chs) {
int count = hm.get(c);
if (count == max) {
System.out.println(c);//D
}
}
}
}
LinkedHashMap
特点
由键决定:有序、不重复、无索引
这里的有序指的是保证存储和取出的元素顺序一致
LinkedHashMap的底层原理
底层数据结构依然是哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序
TreeMap
特点
由键决定特性:可排序、不重复、无索引
可排序:对键进行排序
TreeMap底层原理
TreeMap跟TreeSet底层原理一样,都是红黑树结构
两种排序规则
实现Comparable接口,指定比较规则。
创建集合时传递Comparator比较器对象,指定比较规则。
案例练习
需求1
键:整数表示id
值:字符串表示商品名称
要求:按照id的升序排列、按照id的降序排列
import java.util.TreeMap;
public class Test2 {
public static void main(String[] args) {
//默认升序排列
TreeMap<Integer, String> tm1 = new TreeMap<>();
tm1.put(2, "苹果");
tm1.put(3, "香蕉");
tm1.put(1, "橘子");
tm1.put(4, "梨子");
System.out.println(tm1);//{1=橘子, 2=苹果, 3=香蕉, 4=梨子}
//传递Comparator比较器对象,实现降序排列
TreeMap<Integer, String> tm2 = new TreeMap<>((o1, o2) -> o2 - o1);
tm2.put(2, "苹果");
tm2.put(3, "香蕉");
tm2.put(1, "橘子");
tm2.put(4, "梨子");
System.out.println(tm2);//{4=梨子, 3=香蕉, 2=苹果, 1=橘子}
}
}
需求2
键:学生对象
值:籍贯
要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人
Student类
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人
int i = this.getAge() - o.getAge();
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
测试类
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
TreeMap<Student, String> tm = new TreeMap<>();
Student s1 = new Student("zhangsan", 23);
Student s2 = new Student("lisi", 24);
Student s3 = new Student("wangwu", 25);
tm.put(s2, "上海");
tm.put(s3, "深圳");
tm.put(s1, "北京");
tm.forEach((student, s) -> System.out.println(student + " = " + s));
//Student{name='zhangsan', age=23} = 北京
//Student{name='lisi', age=24} = 上海
//Student{name='wangwu', age=25} = 深圳
Student s4 = new Student("zhangsan", 23);
tm.put(s4, "重庆");
tm.forEach((student, s) -> System.out.println(student + " = " + s));
//Student{name='zhangsan', age=23} = 重庆
//Student{name='lisi', age=24} = 上海
//Student{name='wangwu', age=25} = 深圳
}
}