EnumMap
顾名思义key的对象是enum类型
如果作为key的对象是enum
类型,那么,还可以使用Java集合库提供的一种EnumMap
,它在内部以一个非常紧凑的数组存储value,并且根据enum
类型的key直接定位到内部数组的索引,并不需要计算hashCode()
,不但效率最高,而且没有额外的空间浪费。
public class Main {public static void main(String[] args) {Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);map.put(DayOfWeek.MONDAY, "星期一");map.put(DayOfWeek.TUESDAY, "星期二");map.put(DayOfWeek.WEDNESDAY, "星期三");map.put(DayOfWeek.THURSDAY, "星期四");map.put(DayOfWeek.FRIDAY, "星期五");map.put(DayOfWeek.SATURDAY, "星期六");map.put(DayOfWeek.SUNDAY, "星期日");System.out.println(map);System.out.println(map.get(DayOfWeek.MONDAY));}
}
使用EnumMap
的时候,我们总是用Map
接口来引用它,因此,实际上把HashMap
和EnumMap
互换,在客户端看来没有任何区别。
TreeMap
HashMap
是一种以空间换时间的映射表,它的实现原理决定了内部的Key是无序的,即遍历HashMap
的Key时,其顺序是不可预测的(但每个Key都会遍历一次且仅遍历一次)。
还有一种Map
,它在内部会对Key进行排序,这种Map
就是SortedMap
。注意到SortedMap
是接口,它的实现类是TreeMap
。
SortedMap
保证遍历时以Key的顺序来进行排序。例如,放入的Key是"apple"
、"pear"
、"orange"
,遍历的顺序一定是"apple"
、"orange"
、"pear"
,因为String
默认按字母排序。
public static void main(String[] args) {Map<String, Integer> map = new TreeMap<>();map.put("orange", 1);map.put("apple", 2);map.put("pear", 3);for (String key : map.keySet()) {System.out.println(key);}// apple, orange, pear}
注意点:使用TreeMap
时,放入的Key必须实现Comparable
接口。String
、Integer
这些类已经实现了Comparable
接口,因此可以直接作为Key使用。作为Value的对象则没有任何要求。
如果作为Key的class没有实现Comparable
接口,那么,必须在创建TreeMap
时同时指定一个自定义排序算法
public class TreeMapDemo {public static void main(String[] args) {Map<Person,Integer> map = new TreeMap<>(new Comparator<Person>(){public int compare(Person p1,Person p2){return p1.name.compareTo(p2.name);}});map.put(new Person("Tom"), 1);map.put(new Person("Bob"), 2);map.put(new Person("Lily"), 3);for (Person key : map.keySet()){System.out.println(key);}System.out.println(map.get(new Person("Bob")));}
}
class Person{public String name;Person(String name) {this.name = name;}public String toString() {return "{Person: " + name + "}";}
}
注意到Comparator
接口要求实现一个比较方法,它负责比较传入的两个元素a
和b
,如果a<b
,则返回负数,通常是-1
,如果a==b
,则返回0
,如果a>b
,则返回正数,通常是1
。TreeMap
内部根据比较结果对Key进行排序。
另外,注意到Person
类并未覆写equals()
和hashCode()
,因为TreeMap
不使用equals()
和hashCode()
。
举例,对于Comparator接口要求实现比较方法:
定义了Student
类,并用分数score
进行排序,高分在前
public class Main {public static void main(String[] args) {Map<Student, Integer> map = new TreeMap<>(new Comparator<Student>() {public int compare(Student p1, Student p2) {//重点return p1.score > p2.score ? -1 : 1;}});map.put(new Student("Tom", 77), 1);map.put(new Student("Bob", 66), 2);map.put(new Student("Lily", 99), 3);for (Student key : map.keySet()) {System.out.println(key);}System.out.println(map.get(new Student("Bob", 66))); // null?}
}class Student {public String name;public int score;Student(String name, int score) {this.name = name;this.score = score;}public String toString() {return String.format("{%s: score=%d}", name, score);}
}
运行代码发现new Student("Bob", 66)
进行查找时,结果为null
!
原因出在这个Comparator
上
在p1.score
和p2.score
不相等的时候,它的返回值是正确的,但是,在p1.score
和p2.score
相等的时候,它并没有返回0
!这就是为什么TreeMap
工作不正常的原因:TreeMap
在比较两个Key是否相等时,依赖Key的compareTo()
方法或者Comparator.compare()
方法。在两个Key相等时,必须返回0
。因此,修改代码如下:
public int compare(Student p1, Student p2) {if (p1.score == p2.score) {return 0;}return p1.score > p2.score ? -1 : 1;
}
或者
public int compare(Student p1, Student p2) {return Integer.compare(p1.score,p2.score);}