自平衡二叉树数据结构与TreeMap(TreeSet)排序
SortedMap是Map接口的子接口,而TreeMap是SortedMap接口的实现类。
TreeMap的底层是二叉树,key是无序、不可重复的。
TreeMap的特点是:元素会按照从小到大的顺序排序。
在看TreeMap前,先看看TreeSet,存储在TreeSet中的元素实际上是存储在TreeMap中的key里,而Map集合中起绝对性因素的就是key,所以只要学会了TreeSet,TreeMap也就自然而然的会了。
TreeSet
无序、不可重复;底层为二叉树,元素会排序。
例:
package com.dh.set;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSet01 {
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(1);
treeSet.add(3);
treeSet.add(2);
for (Integer integer : treeSet) {
System.out.println(integer);
}
//字符串会按照字典顺序逐个字母进行比较
TreeSet<String> treeSet1 = new TreeSet<>();
treeSet1.add("hello");
treeSet1.add("world");
Iterator<String> iterator = treeSet1.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
结果:
1
2
3
hello
world
那么二叉树是如何进行排序的呢?
自平衡二叉树数据结构
假设现在有一组数据:100,200,50,60,55,120
往集合中添加数据就是在不停的构造二叉树,最后遍历的时候采用中序遍历(左->根->右)。
100是根,左边的那一串是左子树,左子树中50是根,没有左子树,所以输出50,然后右子树中55是左,50是根,所以依次输出55,60,然后输出根100,然后右子树中,120是左,200是根,所以输出120,200。
所以最后的顺序就是:50,55,60,100,120,200
自定义对象的排序
如果TreeSet中存放的是自己定义的对象,比如说Person,那TreeSet还可以排序吗?
我们可以试试看:
Person类:
package com.dh.set;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试:
package com.dh.set;
import java.util.TreeSet;
public class TreeSet02 {
public static void main(String[] args) {
TreeSet<Person> people = new TreeSet<>();
people.add(new Person("zhangsan",18));
people.add(new Person("lisi",20));
people.add(new Person("wangwu",19));
for (Person person : people) {
System.out.println(person);
}
}
}
结果:
Exception in thread "main" java.lang.ClassCastException: class com.dh.set.Person cannot be cast to class java.lang.Comparable (com.dh.set.Person is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
at java.base/java.util.TreeMap.put(TreeMap.java:536)
at java.base/java.util.TreeSet.add(TreeSet.java:255)
at com.dh.set.TreeSet02.main(TreeSet02.java:9)
结果出现了异常,其实也很容易明白,因为TreeSet不知道怎么比,怎么才是大,怎么才是小,所以就需要我们告诉TreeSet如何去比较Person对象。
异常中出现了java.lang.Comparable,查api文档可以知道这是一个接口,说Person无法转变成java.lang.Comparable,如果Person类实现了java.lang.Comparable接口的话,那是不是就可以转换了呢?
没错!解决方法就是就是实现java.lang.Comparable接口。
实现自定义对象比较规则方法一:实现java.lang.Comparable接口
Person类实现Comparable接口,在compareTo(Object o)中重写比较规则。
package com.dh.set;
public class Person implements Comparable<Person> {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
//升序
return this.age - o.age;
//降序
// return o.age - this.age;
}
}
实现自定义对象比较规则方法二:实现java.util.Comparator接口
Person:
package com.dh.set;
import java.util.Comparator;
public class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
先书写一个比较器,书写一个类实现Comparator接口,并重写compare(Object o1,Object o2),书写比较规则:
package com.dh.set;
import java.util.Comparator;
public class ComparatorRule implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
//升序
return o1.age - o2.age;
//降序
//return o2.age - o1.age;
}
}
TreeSet测试类:
package com.dh.set;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSet02 {
public static void main(String[] args) {
//在实例化TreeSet集合时,将自定义比较器作为参数传进去
TreeSet<Person> people = new TreeSet<>(new ComparatorRule());
people.add(new Person("zhangsan",18));
people.add(new Person("lisi",20));
people.add(new Person("wangwu",19));
for (Person person : people) {
System.out.println(person);
}
}
}
结果:
Person{name='zhangsan', age=18}
Person{name='wangwu', age=19}
Person{name='lisi', age=20}
那么是采用哪种方式更好呢?
采用实现Comparator接口的方式会更加的灵活,因为可以定义多个比较器,需要哪个就传递哪个。