一. TreeSet集合的基本介绍
- TreeSet集合位于java.util包下面。继承了抽象类AbstractSet,并且实现了三个接口NavigableSet,Cloneable, Serializable。
NavigableSet接口继承了SortedSet接口。说明这个集合是可以进行排序的。
2)TreeSet集合是有序,不可重复的。
3)TreeSet集合底层是TreeMap集合的key。TreeSet构造方法调用的就是TreeMap的构造方法。
二.TreeSet集合中的比较器(类继承Comparable接口方式)
1)String,Integer是可以直接排序的。因为它们底层已经实现了排序的规则。
//对String Integer 是可以排序的。
Set<String> strSet = new TreeSet<>();
strSet.add("x");
strSet.add("c");
strSet.add("b");
strSet.add("a");
//遍历集合
for(String str :strSet){
System.out.println(str);//结果为 a,b,c,x
}
2)TreeSet集合想要排序,就要有排序的规则。如果没有排序规则,那么将无法排序,导致报错
//此代码是错误演示!!!!
/*
如果将自定义的类添加进TreeSet集合中,但是没有实现比较规则,则会报错
报错信息: java.lang.ClassCastException
Student cannot be cast to java.lang.Comparable
出现报错的具体错误代码行:
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
*/
TreeSet<Student> stus = new TreeSet<>();
stus.add(new Student("zhangsan",18));
stus.add(new Student("zhangsi",20));
//以下是自定义的学生类
class Student{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
}
3)既然TreeSet集合需要比较规则,那比较规则需要如何定义。我们可以查看TreeSet集合的源码。因为无法添加成功,我们可以认为是在调用add()方法出现的错误。
//添加一个学生类
stus.add(new Student("zhangsan",18));
//查看add(key)方法的源码。
public boolean add(E e) {
//这里又调用了put(key,value)方法
return m.put(e, PRESENT)==null;
}
//在这里,将传进来的参数又到了put()方法中。而这个put()方法,其实是Map集合中的put()方法。
//这里其实调用了TreeMap的put(key value)方法。
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
/*
在put方法中,最终执行了下面的代码。
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
当执行到下面的代码时,将传进来的key值进行了强转。
Comparable<? super K> k = (Comparable<? super K>) key;
此时的key值是我们自定义的学生类,将它强制转化成Comparable类时,因为无法强转,所以出现了错误。
接着看下面的:
cmp = k.compareTo(t.key);
比较的规则就是通过compareTo()这个方法来判断的。
如果比较的值小于零,则将t的左子树赋值给t,如果比较的值大于零,则将t的右子树赋值给t,如果等于零,则意味着有相同的key值,map是不可重复的,所以会将value值覆盖。(这已经和TreeSet并无关系了,这已经是Map集合的key-value的形式)。
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
*/
总结:往TreeSet集合中添加元素时,需要实现比较规则,实现的一种方式就是 添加的元素实现Comparable这个接口,重写里面的compareTo方法(定义比较规则)。String Integer 已经实现了Comparable接口,并且重写了compareTo方法。
TreeSet<Student> stus = new TreeSet<>();
stus.add(new Student("zhangsan",18));
stus.add(new Student("zhangsi",20));
stus.add(new Student("wangwu",19));
for(Student str :stus){
System.out.println(str);//结果为18,19,20
}
//上面是main方法
//////////////////////////////////////
class Student implements Comparable<Student> {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
@Override
public int compareTo(Student o) {
//通过年龄来比较排序
return this.age-o.age;
}
//省略toString()
}
全部源码:
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest001 {
public static void main(String[] args) {
/* //对String Integer 是可以排序的。
Set<String> strSet = new TreeSet<>();
strSet.add("x");
strSet.add("c");
strSet.add("b");
strSet.add("a");
//遍历集合
for(String str :strSet){
System.out.println(str);
}*/
/*
如果将自定义的类添加进TreeSet集合中,但是没有实现比较规则,则会报错
java.lang.ClassCastException:
Student cannot be cast to java.lang.Comparable
*/
TreeSet<Student> stus = new TreeSet<>();
stus.add(new Student("zhangsan",18));
stus.add(new Student("zhangsi",20));
stus.add(new Student("wangwu",19));
for(Student str :stus){
System.out.println(str);
}
}
}
class Student implements Comparable<Student> {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
return this.age-o.age;
}
}
三.TreeSet集合的比较器(自定义比较器)
1)之前调用的TreeSet的构造方法是无参的。
public TreeSet() {
//底层其实是构造了一个TreeMap
this(new TreeMap<E,Object>());
}
public TreeMap() {
/*
无参的TreeMap构造方法给实例变量comparator赋值为null。
private final Comparator<? super K> comparator;
public interface Comparator<T> {}
查看源码可以知道comparator是一个Comparator接口
*/
comparator = null;
}
/*
*/
2)TreeSet的其他构造方法
1.TreeSet(Comparator<? super E> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。(这个也是我们现在需要用到的构造方法
2.TreeSet(Collection<? extends E> c)
构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序*进行排序。
3.TreeSet(SortedSet<E> s)
构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet。
package test1209_Javase_TreeSet;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest002 {
public static void main(String[] args) {
/*
TreeSet<User> treeSet = new TreeSet<>();
这是无参构造方法
*/
MyComparator myCom =new MyComparator();
//自定义的MyComparator类,实现了Comparator接口。
TreeSet<User> treeSet = new TreeSet<>(myCom);
/*
TreeSet底层就是TreeMap,构造方法底层是调用TreeMap的构造方法,add()方法底层是调用put()方法
在TreeMap的构造方法中,这个比较器被接收到了。
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
*/
treeSet.add(new User(20,"zhangsan"));
treeSet.add(new User(10,"zhangsi"));
treeSet.add(new User(15,"lisi"));
/*
调用add方法,就是调用put方法,最后进入下面的put源码:
这时候的比较器comparator已经通过构造方法传进来,所以进入if语句。
在if语句中,调用了compare方法进行比较,其中两个key就是我们add()传进来的参数。
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
*/
for (User user:treeSet) {
System.out.println(user);
/*输出结果为:
10 zhangsi
15 lisi
20 zhangsan
*/
}
}
}
class User{
int id;
String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
class MyComparator implements Comparator<User>{
@Override
public int compare(User o1, User o2) {
//根据用户的id值大小排序,可以自定义其他排序规则
return o1.id- o2.id;
}
}