目录
一、Map体系概述
Map系列集合是以键值对的形式存储,特点都是由键决定的,值只是一个附属品,不做要求。
我们常用的有 HashMap LinkedHashMap TreeMap 三种 , 其中:
HashMap(由键决定特点):无序、不重复、无索引;(用的最多)
LinkedHashMap (由键决定特点):由键决定的特点:有序、不重复、无索引。
TreeMap(由键决定特点):按照大小默认升序排序、不重复、无索引
二、Map集合
1.方法及创建
①常用方法
V put(K key,V value) | 添加元素 |
V remove(object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(object key) | 判断集合是否包含指定的键 |
boolean containsValue(object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度即集合中键值对的个数 |
②创建及方法代码示例
//1.创建Map集合的对象
Map<String,String> m= new HashMap<>();
//2.添加元素
//put方法的细节:
//添加/覆盖
//在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中,方法返回nu11
//在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回。
m.put("白月初","苏苏");
m.put("东方月初","红红");
m.put("戴沐白","朱竹清");
String value = m.put("白月初", "涂山苏苏");
System.out.println(value);//返回覆盖的值 苏苏
//删除
String result = m.remove("东方月初");
System.out.println(result); //返回 删除键的值 红红
//清空
//m.clear();
//判断是否包含 键
boolean keyResult = m.containsKey("戴沐白");
System.out.println(keyResult);//true
//是否包含 值
boolean valueResult = m.containsValue("朱竹清");
System.out.println(valueResult);//true
boolean empty = m.isEmpty();
System.out.println(empty);//false
int size = m.size();
System.out.println(size);//2
//3.打印结合
System.out.println(m);//{白月初=涂山苏苏, 戴沐白=朱竹清}
2.遍历
①键遍历
//1.创建Map集合的对象
Map<String,String> map= new HashMap<>();
//2.添加元素
map.put("白月初","苏苏");
map.put("东方月初","红红");
map.put("戴沐白","朱竹清");
//3.通过键找值
//3.1获取所有的键,把这些键放到一个单列集合当中
Set<String> keys = map.keySet();
//3.2遍历单列集合,得到每一个键
for (String key : keys){
//3.3利用map集合中的键获取对应的值 get
String value = map.get(key);
System.out.println(key+"="+value);
}
/* 运行结果:
白月初=苏苏
东方月初=红红
戴沐白=朱竹清
*/
②键值对对象遍历
//Map创建以及添加元素同上
//3.通过键值对对象进行遍历
//3.1通过一个方法获取所有的键值对对象,返回一个Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
//3.2遍历entries这个集合,去得到里面的每一个键值对对象
for (Map.Entry<String, String>entry :entries){ //entries 可以换为map.entrySet()
//3.3利用entry调用get方法获取键和值
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"="+value);
}
③lambda表达式
//Map创建及添加元素同上
//3.利用lambda表达式进行遍历
/*map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String s, String s2) {
System.out.println(s+"="+s2);
}
});*/
//省略形式:
map.forEach( (s, s2) -> System.out.println(s+"="+s2));
/*运行结果:
白月初=苏苏
东方月初=红红
戴沐白=朱竹清
*/
三、HashMap
1.概述
HashMap是Map里面的一个实现类,继承Map里面的方法。
特点都是由键决定的:无序、不重复、无索引
HashMap跟HashSet底层原理是一模一样的,都是哈希表结构
依赖hashcode方法和equals方法保证键的唯一
如果键存储的是自定义对象,需要重写hashCode和equals方法
如果值存储自定义对象,不需要重写hashCode和equals方法
2.键存储自定义对象示例及遍历方法
需求:创建一个HashMap集合,键是学生对象(student),值是籍贯(string)。
存储三个键值对元素,并遍历
要求:同姓名,同年龄认为是同一个学生
①Main.java
/*核心点:
HashMap的键位置如果存储的是自定义对象,需要重写hashcode和equals方法。
*/
//1.创建HashMap的对象
HashMap<Student,String> hm = new HashMap<>();
//2.创建三个学生对象
Student s1 = new Student("zhagnsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("wangwu",25);
//3.添加元素
hm.put(s1,"江苏");
hm.put(s2,"浙江");
hm.put(s3,"福建");
hm.put(s4,"山东");//键唯一 会被覆盖
//4.遍历集合
//一 键遍历
for (Student key : hm.keySet()){
String value = hm.get(key);
System.out.println(key+"="+value);
}
System.out.println("-----------------");
//二 lambda表达式
hm.forEach((Student student, String s) -> System.out.println(student+"="+s));
System.out.println("--------------------");
//三 键值对遍历
for (Map.Entry<Student, String> entry : hm.entrySet()) {
Student key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"="+value);
}
/*各个遍历结果都为:
Student{name = wangwu, age = 25}=山东
Student{name = lisi, age = 24}=浙江
Student{name = zhagnsan, age = 23}=江苏
*/
②Student类
//重写了equals方法
public class Student {
private String name;
private int age;
//JavaBean自动生成构造省略······
//重写的方法
@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);
}
}
四、LinkedHashMap
1.概述
由键决定:有序、不重复、无索引。这里的有序指的是保证存储和取出的元素顺序一致。
原理:底层数据结构依然是哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
2.代码演示
//1.创建集合
LinkedHashMap<String,Integer> lhm = new LinkedHashMap<>();
//2.添加元素
lhm.put("a",123);
lhm.put("a",123);//不会重复添加
lhm.put("a",111);//会覆盖
lhm.put("b",456);
lhm.put("c",789);
//3.打印集合
System.out.println(lhm);//{a=111, b=456, c=789}
五、TreeMap
1.概述
TreeMap跟TreeSet底层原理一样,都是红黑树结构的。
由键决定特性:不重复、无索引、可排序(对键进行排序)
注意:默认按照键的从小到大进行排序,也可以自己规定键的排序规则
代码书写两种排序规则(如果两种排序规则都写了,会以第二种为主)
①实现Comparable接口,指定比较规则。
②创建集合时传递Comparator比较器对象,指定比较规则。
2.排序演示
①实现Comparable接口
要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人。
Main.java
//1.创建集合
TreeMap<Student,String>tm = new TreeMap<>();
//2.创建学生对象
Student s1 = new Student("zhangsan",23);
Student s3 = new Student("wangwu",25);
Student s2 = new Student("lisi",24);
Student s4 = new Student("lisi",24);
//3.添加元素
tm.put(s1,"江苏");
tm.put(s2,"天津");
tm.put(s3,"北京");
tm.put(s4,"南京");
//4.打印
System.out.println(tm);
/*运行结果:
{Student{name = zhangsan, age = 23}=江苏, Student{name = lisi, age = 24}=南京, Student{name = wangwu, age = 25}=北京}
*/
Student类
public class Student implements Comparable<Student>{ //实现Comparable接口
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;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
//重写 compareTo方法
@Override
public int compareTo(Student o) {
//按照学生年龄的升序排列,年龄一样按照字母排列,同姓名年龄视为同一个人。
//this:表示当前要添加的元素
//o: 表示已经在红黑树中存在的元素
//返回值:
//负数:表示要添加的元素是小的 存左边
//正数:表示要添加的元素是大的,存右边
//0:表示当前要添加的元素已经存在 舍弃
int i= this.getAge()-o.getAge();
i = i == 0?this.getName().compareTo(o.getName()):i;
return i;
}
}
②使用Comparator比较器对象
//Integer、Double 默认情况下都是按照升序排列的
//String 按照字母再ASCII码表中对应的数字升序排列
//1.创建集合对象
TreeMap<Integer,String> tm =new TreeMap<>();
//2.添加元素
tm.put(5,"ab");
tm.put(4,"cd");
tm.put(3,"ef");
tm.put(2,"hi");
tm.put(1,"gk");
System.out.println(tm); //默认升序排序
//要求:按照降序排序 使用比较器对象
TreeMap<Integer,String> tm2 =new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//o1:当前要添加的元素
//o2:表示已经在红黑树中存在的元素
return o2-o1;
}
});
//2.添加元素
tm2.put(5,"ab");
tm2.put(4,"cd");
tm2.put(3,"ef");
tm2.put(2,"hi");
tm2.put(1,"gk");
System.out.println(tm2);
/*运行结果:
{1=gk, 2=hi, 3=ef, 4=cd, 5=ab}
{5=ab, 4=cd, 3=ef, 2=hi, 1=gk}
*/
六、思考
1.HashMap是哈希表结构的,JDK8开始由数组,链表,红黑树组成的。既然有红黑树,Hashmap的键是否需要实现compareable接口或者传递比较器对象呢?
不需要的。因为在Hashmap的底层,默认是利用哈希值的大小关系来创建红黑树的
2.TreeMap和HashMap谁的效率更高?
如果是最坏情况,添加了8个元素,这8个元素形成了链表,此时TreeMap的效率要更高,但是这种情况出现的几率非常的少。一般而言,还是Hashmap的效率要更高。
(JDK8以后,Hash表底层当链表长度大于8而且数组长度大于等于64,链表自动转为红黑树)
3.三种双列集合,如何选择?(HashMap linkedHashMap TreeMap)
默认:HashMap(效率最高)
如果要保证存取有序:LinkedHashMap
如果要进行排序:TreeMap