一、TreeMap 实现排序
TreeMap是一个有序结构,TreeSet也是类似。
他们可以实现对元素的排序。TreeMap 是针对 key进行排序。
如果TreeMap的key 是Integer类型,可以无需指定任何特殊条件,默认即按照升序进行排序,如:
public static void main(String[] args) {Object value = new Object();TreeMap<Integer, Object> intMap = new TreeMap<>();intMap.put(7, value);intMap.put(4, value);intMap.put(0, value);intMap.put(10, value);System.out.println(intMap);}
// 输出:
{0=java.lang.Object@4554617c, 4=java.lang.Object@4554617c, 7=java.lang.Object@4554617c, 10=java.lang.Object@4554617c}
那么如果想降序,或者key不是一个 Integer类型,该如何排序?
这时我们需要指定一个比较器:
public static void main(String[] args) {Object value = new Object();TreeMap<Integer, Object> intMap = new TreeMap<>((o1, o2) -> o2 - o1);intMap.put(7, value);intMap.put(4, value);intMap.put(0, value);intMap.put(10, value);System.out.println(intMap);}
// 输出:
{10=java.lang.Object@f6f4d33, 7=java.lang.Object@f6f4d33, 4=java.lang.Object@f6f4d33, 0=java.lang.Object@f6f4d33}
这里有一个口诀可以快速记忆升序还是降序的比较器,我们知道比较器需要接收两个参数 o1 和 o2,那么如果是 o1 - o2 其排序结果就是升序,如果 o2 - o1 其排序结果就是降序:
排序器升降记忆口诀:
正减升,反减降
对于一个POJO 类型,如果有一个 Integer 类型的 age 属性,希望用 age 排序,就可以是 o1.getAge() - o2.getAge() ,这就是升序,降序就是颠倒过来。
二、不添加“重复”的 key
注意,TreeMap 通过key 的大小来判定顺序,它认为的 “重复” key 指的是大小比较相等的key,如果相等,便不会重复添加元素,这与HashMap 覆盖旧值是有区别的。
public static void main(String[] args) {Student s1 = new Student(1, "s1", 24);Student s2 = new Student(2, "s2", 30);Student s3 = new Student(3, "s3", 21);Student s4 = new Student(4, "s3", 21);// TreeMap通过 key来进行排序,如果是可比较类型,直接排序// 如果不是可比较类型,需要令该类型实现Comparable接口,或构造器中指定排序器TreeMap<Student, String> stuMap = new TreeMap<>((o1, o2) -> o1.getAge() - o2.getAge());Comparator<? super Student> comparator = stuMap.comparator();stuMap.put(s2, s2.getName());stuMap.put(s3, s3.getName());stuMap.put(s1, s1.getName());stuMap.put(s4, s4.getName());System.out.println(stuMap);}
输出结果:
{Student{id=3, name='s3', age=21}=s3, Student{id=1, name='s1', age=24}=s1, Student{id=2, name='s2', age=30}=s2}
可以看到由于 age = 21 的Student有s3 和 s4,但却没有添加 s4。
如果“重复”了,却又想保留“重复”元素,原因 它们虽然排序属性相等,但并不是完全相同的两个对象,该如何做?
可以使用hashCode(但是存在冲突的风险),或直接使用内存地址来进一步比较。
三、比较器优先
对于POJO 实现了 Comparable 的情况,我们需要实现它的 compareTo 方法:
class Student implements Comparable<Student> {private Integer id;private String name;private Integer age;@Overridepublic int compareTo(Student o) {return this.age - o.getAge();}// 构造器和 getter、setter
}
那么 o1 就是当前 this 对象,o2 就是参数对象,同样遵循 “正减升,反减降”的口诀。
不过如果同时指定了排序器和 Comparable ,而这两种逻辑又存在冲突,该如何选择呢?
答案是比较器优先,原因是比较器的位置更接近客户代码,在排序前指定排序规则,要比类定义时指定排序规则更加明确,因此 TreeMap 一类有序结构都是按照排序器优先的原则来排序的。