从Java中TreeMap集合来引出外部比较器和内部比较器的一些用法
引出:从TreeMap的put方法中来看
在往TreeMao集合里添加元素的时候,会进行比较添加元素的key值,这时就会用到比较器,在源码中有两种比较器。
将元素的key值进行比较,调用key值自己的compareTo 这有两种情况:
[1] key值自己是系统定义好的,像Integer、String等,那么Integer或String本身自带compareTo方法,然后来进行比较
- 从代码中看
public class Test01 {
public static void main(String[] args) {
//这里的key是String它自身带了比较器即比较的方法,因此不需要自己来弄比较器
TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Tom",101);
treeMap.put("Confidence",101);
treeMap.put("Excellent",101);
treeMap.put("Apple",101);
treeMap.put("Google",101);
System.out.println(treeMap);
}
}
/*运行结果:
*{Apple=101, Confidence=101, Excellent=101, Google=101, Tom=101}
*Process finished with exit code 0
[2] key值是自己重新定义的一个类,那么这个类一开始是没有compareTo方法的,那么它就得自己弄一个compareTo方法。
方法:自己定义的key值类先实现一个接口,分为两种情况
(1)自定义类实现Comparable<key的自定义类>接口,这种被称为内部比较器,在创建TreeMap集合对象时不用往参数里添加比较的那个对象。
- 从代码中看:
public class Student implements Comparable<Student>{
private int Std;
private String name;
//这个类其他方法先省略,只看实现Comparable接口的compareTo方法
//如这个方法所示,比较了Student类的对象的std属性,参数是Student对象,最后用int返回
@Override
public int compareTo(Student o) {
return this.getStd()-o.getStd();
}
}
import java.util.TreeMap;
public class Test01 {
public static void main(String[] args) {
//这里由于是Student类自己内部定义好了比较的方法,因此构造器这里不用传参数即比较器对象。
TreeMap<Student, Integer> map = new TreeMap<>();
map.put((new Student(105,"Tom")),103);
map.put((new Student(103,"Tqm")),103);
map.put((new Student(102,"Trm")),104);
map.put((new Student(108,"Txm")),106);
System.out.println(map);
}
}
/*运行结果:
*{Student{Std=102, name='Trm'}=104, Student{Std=103, name='Tqm'}=103, Student{Std=105, name='Tom'}=103, Student{Std=108, name='Txm'}=106}
*Process finished with exit code 0
* */
(2)自定义类实现Comparator<key的自定义类>接口,这种被称为外部比较器,在创建TreeMap集合对象时需要往参数里添加比较器的对象
- 从代码来看
- 方法一:
public class Student {
//为了节约空间,这个类在这里只显示了它的属性
private int Std;
private String name;
}
//自己定义一个比较类名字为CompareName
class CompareName implements Comparator<Student>{
//这个类实现了Comparator接口,它是一个外部比较器的接口,
//如下面方法所示,这个compare方法用来比较传进参数的两个Student对象的name属性
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
public class Test01 {
public static void main(String[] args) {
//创建比较类的对象
CompareName cn = new CompareName();
//在创建对象时,传入比较类的对象,因为到时候要调用这个比较类对象里的比较方法
TreeMap<Student, Integer> map = new TreeMap<>(cn);
map.put((new Student(105,"Tom")),103);
map.put((new Student(103,"Cat")),103);
map.put((new Student(102,"Excellent")),104);
map.put((new Student(108,"Legend")),106);
map.put((new Student(108,"Apple")),106);
System.out.println(map);
}
}
/*运行结果:
* {Student{Std=108, name='Apple'}=106, Student{Std=103, name='Cat'}=103,
* Student{Std=102, name='Excellent'}=104, Student{Std=108, name='Legend'}=106,
* Student{Std=105, name='Tom'}=103}
* Process finished with exit code 0
* */
- 可能会有个疑问,为啥在创建TreeMap集合对象时可以传进自己定义那个类的对象呢?
下面我们从源码来看:
- 方法二:其实跟方法一差不多 只不过是在定义 比较类的时候直接在创建TreeMap集合对象那里进行操作,如下面代码所示:
public class Student {
//为了节约空间,这个类在这里只显示了它的属性
private int Std;
private String name;
}
import java.util.Comparator;
import java.util.TreeMap;
public class Test01 {
public static void main(String[] args) {
/*既然这里构造器的参数为一个Comparator接口声明的一个自己定义比较类的对象
* 那么这里直接new一个岂不是更方便,虽然操作有点诡异,但是可以节约内存,每次指向完后
* 直接释放掉内存,这里即用到了匿名内部类的操作,这里直接重写Comparator接口下的compare方法
* 参数是两个Student对象,返回一个int类型的数值,这里重写的方法是比较两个Student对象的name属性*/
TreeMap<Student, Integer> map = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
});
map.put((new Student(105,"Tom")),103);
map.put((new Student(103,"Cat")),103);
map.put((new Student(102,"Excellent")),104);
map.put((new Student(108,"Legend")),106);
map.put((new Student(108,"Apple")),106);
System.out.println(map);
}
}
/*运行结果:
* {Student{Std=108, name='Apple'}=106, Student{Std=103, name='Cat'}=103,
* Student{Std=102, name='Excellent'}=104, Student{Std=108, name='Legend'}=106,
* Student{Std=105, name='Tom'}=103}
* Process finished with exit code 0
* */