映射(Map)
键值对
Map是一个接口 —— 常用的实现类有HashMap、TreeMap
一、 HashMap
最基本、最常用的一种Map
1. 如何创建Map
List
- 添加 —— add(obj)
- 获取 —— get(obj)
Map
- 添加<key, value> —— put(key, value)
- 用key获取value —— get(key)
//HashMap<key, value>
public class Test01 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap();
map.put("小明",15);//姓名、年龄
map.put("小红",14);
map.put("小刘",18);
map.put("小王",20);
String[] man = "小刘,小王,小强".split(",");
for (String s : man) {
System.out.println(s + map.get(s) + "岁了");
}
}
}
/*
小刘18岁了
小王20岁了
小强null岁了
*/
-
小强之所以为null岁是因为get的键不存在,返回null
-
因此需要先判断
get()的返回值,是否为null,而不是直接使用for (String s : man) { if(map.get(s)!=null) { System.out.println(s + map.get(s) + "岁了"); } } /* 小刘18岁了 小王20岁了 */
-
2. 如何遍历Map
两种方式
Map
public class Test02 {
public static void main(String[] args) {
//泛型中都是引用对象,因此只能是Character,不能是char
Map<Character, Double> map = new HashMap();
map.put('b',6.66);
map.put('a',3.14);
map.put('d',13.14);
map.put('c',9.99);
//keySet()方法返回,map中的所有的键
for (Character key : map.keySet()) {
System.out.print(key+" = ");//获取键
System.out.println(map.get(key));//获取值
}
}
}
/*
a = 3.14
b = 6.66
c = 9.99
d = 13.14
*/
-
虽然全部输出了,但不是按照put的顺序输出的
-
因为put(key, value)后,计算其对应的hash值,存放到对应的位置
-
因此无法保证hashmap是有序的
-
Entry
public class Test02 {
public static void main(String[] args) {
Map<Character, Double> map = new HashMap();
map.put('b',6.66);
map.put('a',3.14);
map.put('d',13.14);
map.put('c',9.99);
//entrySet()
for (Map.Entry<Character,Double> entry : map.entrySet()) {
String s = String.format("%c = %.2f",entry.getKey(),entry.getValue());
System.out.println(s);
}
}
}
/*
a = 3.14
b = 6.66
c = 9.99
d = 13.14
*/
-
entry返回一个集合
3. 自定义类作为key
内存机制
public class Test02 {
public static void main(String[] args) {
Map<Character, Double> map = new HashMap();
map.put('b',6.66);
map.put('a',3.14);
map.put('d',13.14);
map.put('c',9.99);
Character character = new Character('c');
//两个c不是同一个对象,但依旧可以获得对应的value值
System.out.println(map.get(character));//9.99
}
}
-
由此可以得出键值比较不是用
==比较的,而是用equals()比较的 -
键值比较是equals实现的
自定义的类为Key
之前的key都是系统提供的类,如果使用用户自定义的类作为key该如何判断
-
重写equals()和hashcode
-
由于Object的hashCode返回的是对象的hash值,所以即使equals返回TRUE,集合也可能判定两个对象不等,所以必须重写hashCode方法,以保证当equals返回TRUE时,hashCode也返回Ture,这样才能使得集合中存放的对象唯一。
-
public class Test03 { public static void main(String[] args) { Map<Player, String> map = new HashMap(); //人,职业 map.put(new Player("小明",15,"唱",2),"歌唱家"); map.put(new Player("小红",14,"跳",3),"舞蹈家"); map.put(new Player("小刘",18,"rap",4),"说唱歌手"); map.put(new Player("小王",20,"篮球",5),"运动员"); Player man = new Player("小明",15,"唱",2); //如果不重写equals和hashcode会返回null System.out.println(map.get(man));//歌唱家 } } class Player { String name; int age; String hobby; double grade; public Player(String name, int age, String hobby, int grade) { this.name = name; this.age = age; this.hobby = hobby; this.grade = grade; } @Override public String toString() { String s = String.format("%d岁上%d年级的%s喜欢%s",age, grade,name,hobby); return s; } @Override public int hashCode() { int hash = 0; hash += hash * 66 + name.hashCode(); hash += hash * 66 + age; hash += hash * 66 + hobby.hashCode(); hash += hash * 66 + Double.doubleToLongBits(grade); return hash; } @Override public boolean equals(Object obj) { boolean nameTF; boolean ageTF; boolean hobbyTF; boolean gradeTF; Player player = (Player)obj; if (player.age == this.age) { ageTF = true; } else { ageTF = false; } if (player.name.equals(null)){ nameTF = false; } else if (player.name.equals(this.name)) { nameTF = true; } else { nameTF = false; } if (player.hobby.equals(null)){ hobbyTF = false; } else if (player.hobby.equals(this.hobby)) { hobbyTF = true; } else { hobbyTF = false; } if (player.grade == this.grade) { gradeTF = true; } else { gradeTF = false; } return ageTF && hobbyTF && nameTF && gradeTF; } }
-
二、 EnumMap
如果key是Enum 可以使用 EnumMap
EnumMap初始化的时候需要使用有参构造
public class Test04 {
public static void main(String[] args) {
//等号另一侧new会自动补充泛型
Map<DayOfWeek,String> map = new EnumMap<DayOfWeek, String>(DayOfWeek.class);
map.put(DayOfWeek.MONDAY,"周一");
System.out.println(map.get(DayOfWeek.MONDAY));//周一
}
}
三、 TreeMap
hashMap是按照
计算出hash值顺序存储的,而不是按照put(key,value)进入的先后排序的,因此为无序的
1. TreeMap是有序map——自定义排序
而不是所谓的先进先出
public class Test05 {
public static void main(String[] args) {
//无序Map
System.out.println("=============hashMap=============");
Map<String, String> map = new HashMap();
map.put("一号","小明");
map.put("二号","小红");
map.put("三号","小刘");
map.put("四号","小王");
for (String key : map.keySet()) {
System.out.print(key);//键
System.out.println(map.get(key));//值
}
//有序Map
System.out.println("=============treeMap=============");
Map<String,String> seqMap = new TreeMap<>();
seqMap.put("一号","小明");
seqMap.put("二号","小红");
seqMap.put("三号","小刘");
seqMap.put("四号","小王");
for (String key : seqMap.keySet()) {
System.out.print(key);
System.out.println(seqMap.get(key));
}
}
}
/*
=============hashMap=============
二号小红
四号小王
三号小刘
一号小明
=============treeMap=============
一号小明
三号小刘
二号小红
四号小王
*/
-
之所以能排序的原因——treeMap的put方法实现了用到了Comparator接口、comparable接口
TreeMap源码
2. 自定义的类
系统提供的类都重写了comparable接口的方法,因此这些类是可以在TreeMap中进行比较排序的。
-
String
-
Integer

而由于自定义的类不是一个可以比较的对象,在TreeMap中会抛出异常
-
把Map里的四个人按年龄从小到大排序
public class Test06 { public static void main(String[] args) { Map<Person,String> map = new TreeMap<>(); map.put(new Person("小明",15),"歌唱家"); map.put(new Person("小红",14),"舞蹈家"); map.put(new Person("小刘",18),"说唱歌手"); map.put(new Person("小王",20),"运动员"); for (Map.Entry<Person,String> entry : map.entrySet()) { System.out.println(entry); } /* 在java10中引入了var,可以由编译器判断类型 for (var entry : map.entrySet()) { System.out.println(entry); } */ } } class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { String s = String.format("%d岁的%s",this.age,this.name); return s; } }-
抛出异常

-
解决方案
-
把Person这个自定义类去实现comparable接口
//如果不约束Conparable接口的泛型,CompareTo方法的参数默认为Objeact类型 //需要先强制转换成Person类型,即Person person = (Person)o; class Person implements Comparable<Person> { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { String s = String.format("%d岁的%s",this.age,this.name); return s; } //compare,比较此对象与指定对象的顺序,如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。 @Override public int compareTo(Person o) { //返回的值>0则说明,这个对象就大于当前的对象 return this.age-o.age; } } /* 14岁的小红=舞蹈家 15岁的小明=歌唱家 18岁的小刘=说唱歌手 20岁的小王=运动员 */ -
写一个比较器的实现类作为TreeMap的参数
public class Test06 { public static void main(String[] args) { //创建比较器 PersonName personName = new PersonName(); //在TreeMap的有参构造,放入比较器 Map<Person,String> map = new TreeMap<>(personName); map.put(new Person("小明",15),"歌唱家"); map.put(new Person("小红",14),"舞蹈家"); map.put(new Person("小刘",18),"说唱歌手"); map.put(new Person("小王",20),"运动员"); for (Map.Entry<Person,String> entry : map.entrySet()) { System.out.println(entry); } } } class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { String s = String.format("%d岁的%s",this.age,this.name); return s; } } //比较器 class PersonName implements Comparator<Person> { @Override public int compare(Person o1, Person o2) { //o2.age-o1.age就是逆序 return o1.age-o2.age; } } /* 14岁的小红=舞蹈家 15岁的小明=歌唱家 18岁的小刘=说唱歌手 20岁的小王=运动员 */-
compare方法比较o1和o2的大小,然后返回一个int类型的值
- 返回值"<0"代表“o1比o2小”,"=0"代表“o1等于o2”,">0"代表“o1比o2大”
-
为了方便也可以不写外部类,把比较器写成匿名内部类
public class Test06 { public static void main(String[] args) { //在TreeMap的有参构造,放入比较器 Map<Person,String> map = new TreeMap<>(new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.age-o2.age; } }); map.put(new Person("小明",15),"歌唱家"); map.put(new Person("小红",14),"舞蹈家"); map.put(new Person("小刘",18),"说唱歌手"); map.put(new Person("小王",20),"运动员"); for (Map.Entry<Person,String> entry : map.entrySet()) { System.out.println(entry); } } } class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { String s = String.format("%d岁的%s",this.age,this.name); return s; } } /* 14岁的小红=舞蹈家 15岁的小明=歌唱家 18岁的小刘=说唱歌手 20岁的小王=运动员 */
-
-
-
3. Comparable接口和Comparetor的比较
- Comparable相当于内部比较器
- 比较简单,但需要修改源代码
- 只要实现Comparable 接口的对象直接就成为一个可以比较的对象
- Comparator相当于外部比较器
- 自定义程度高,不需要修改源代码,而是另外实现一个比较器
- 在这个比较器中,可以自己实现复杂的可以通用的逻辑,还可以把比较器和对象一起传递
- 自定义程度高,不需要修改源代码,而是另外实现一个比较器
四、LinkedHashMap
TreeMap虽然是有序Map,但是只能进行排序,仍无法做到FIFO——按put的顺序输出
- LinkedHashMap也是有序Map,但是LinkedHashMap内元素顺序只和插入顺序有关
public class Test07 {
public static void main(String[] args) {
Map<Person,String> map = new LinkedHashMap<>();
map.put(new Person("小明",15),"歌唱家");
map.put(new Person("小红",14),"舞蹈家");
map.put(new Person("小刘",18),"说唱歌手");
map.put(new Person("小王",20),"运动员");
for (Map.Entry<Person,String> entry : map.entrySet()) {
System.out.println(entry);
}
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
String s = String.format("%d岁的%s",this.age,this.name);
return s;
}
}
/*
15岁的小明=歌唱家
14岁的小红=舞蹈家
18岁的小刘=说唱歌手
20岁的小王=运动员
*/

浙公网安备 33010602011771号