TreeSet集合
1、TreeSet
集合底层实际上是一个TreeMap
,TreeMap
底层是一个二叉树。放到TreeSet
集合中的元素等同于放到TreeMap
集合的key
部分了。
2、TreeSet
集合中元素:无序不可重复,但是可以按照元素的大小顺序自动排序(按照添加对象的指定属性进行排序),称为可排序集合。添加的是同类对象时才会排序。
排序的两种方法式:自然排序(实现Comparable)和 定制排序(Comparator)。
TreeSet
比较两个对象是否相同的标准为:compareTo()
返回 0,不再是equals()
。
3、TreeSet
集合用处:编写程序从数据当中取出数据,在页面展示用户信息的时候按照生日升序或者降序,这个时候可以使用TreeSet
集合,因为TreeSet
集合放进去,拿出来就是有顺序的。
//创建一个TreeSet集合
TreeSet<String> ts=new TreeSet<>();
//添加String
ts.add("zhangsan");
ts.add("lisi");
ts.add("wangwu");
ts.add("wangliu");
ts.add("zhangsi");
//遍历
for(String s:ts){
//按字典升序排序
System.out.println(s);
}
结果: lisi wangliu wangwu zhangsan zhangsi
对自定义类型放入TreeSet集合中会排序吗?
class Person{
int age;
public Person(int age){
this.age=age;
}
//重写toString()方法
public String toString(){
return "Person[age="+age+"]";
}
}
//自定义类型放入TreeSet集合中会排序吗?
Person p1=new Person(32);
Person p2=new Person(20);
Person p3=new Person(25);
TreeSet<Person> persons=new TreeSet<>();
persons.add(p1);
persons.add(p2);
persons.add(p3);
//遍历
for(Person p:persons){
System.out.println(p);
}
报错:Person cannot be cast(转换) to class java.lang.Comparable
TreeMap
集合的put()
添加元素的方法,当key==null
(加入的key
值部分为空)时:
retern t.setValue(value);
此时新的key
值对应的value
值直接将旧key
值对应的value
值覆盖了,不用调用equals
方法判断key
值是否相等。
排序的实现:新加入的key
值与集合中已经存在的key
值用Comparable
进行比较。
报错原因分析:由于Preson
类中没有实现java.lang.Comparable
的compareTo()
比较方法,所以加入的新值无法和集合中的值进行比较,从而无法实现排序。
class Person implements Comparable<Preson>{
int age;
public Person(int age){
this.age=age;
}
//重写compareTo()方法
public int compareTo(Person p){
/*p1.compareTo(p2): this就是p1,p就是p2*/
/* int age1=this.age;
int age2=p.age;
if(age1==age2){
return 0;
}else if(age1>age2){
return 1;
}else{
return -1;
}
*/
return this.age-p.age; //此时是升序排序
}
//重写toString()方法
public String toString(){
return "Person[age="+age+"]";
}
}
//自定义类型放入TreeSet集合中会排序吗?
Person p1=new Person(32);
Person p2=new Person(20);
Person p3=new Person(25);
TreeSet<Person> persons=new TreeSet<>();
persons.add(p1);
persons.add(p2);
persons.add(p3);
//遍历
for(Person p:persons){
System.out.println(p);
}
先按照年龄升序,如果年龄相同时按名字排序。
class Person implements Comparable<Preson>{
int age;
String name;
//构造方法省略
public String toString(){
return "Person{"+"name'"+name+"\'"+",age="+age+'}';
}
public int compareTo(Person p){
if(this.age==p.age){
//年龄相同时按照名字排序,String类型重写了compareTo()方法
return this.name.compareTo(p.name);
}else{
return this.age-p.age;
}
}
}
//当类中实现没有泛型时:
public class User implements Comparable{}
//compareTo()的第二种写法:
public int compareTo(Object o){
if(o instanceof User){//判断对象是否属于相同类型
User user=(User)o;
//从大到小排序(默认是从小到大排)
return -this.name.compareTo(user.name);
int compare = -this.name.compareTo(user.name);
if(compare != 0){ //compare=0:表示两个对象相等
return compare; //当两个对象名字不同时用名字比较大小
}else{ //当两个对象名字相同时用年龄比较大小
return Integer.compare(this.age,user.age);
}
}else{
throw new RuntimeException("输入的类型不匹配");
}
}
compareTo()
方法的返回值很重要:
返回0表示相同,value 会覆盖;返回>0,会继续在右子树上找(5-3=2>0,说明左边这个数字比较大,所以在右子树上找);返回 <0,会继续在左子树上找。
4、TreeSet/TreeMap
是自平衡二叉树,遵循左小右大原则存放,每次加入新数据都要从根节点开始从上往下进行比较,小的存放在左子树,大的存放在右子树。
遍历二叉树的方式有前序遍历:根左右;中序遍历:左根右;后序遍历:左右根 ,TreeSet/TreeMap 集合采用的是中序遍历,Iterator
迭代器采用的是中序遍历。
5、TreeSet
集合中元素的第二种排序方式:使用比较器的方式。
class WuGui{
int age;
public WuGui(int age){
this.age=age;
}
public String toString(){
return "小乌龟["+"age"+age+']';
}
}
//单独编写一个比较器实现java.util.Comparator接口(Comparable是java.lang包下的)
class WuGuiComparator implements Comparator<WuGui>{
public int compare(WuGui w1,WuGui w2){
//指定按照年龄排序
return w1.age-w2.age;
}
}
public static void main(String[] args){
//创建TreeSet集合的时候,需要使用这个比较器
/*
TreeSet<WuGui> wuGui=new TreeSet<>();
这样创建对象没有通过构造方法传递一个比较器进去
*/
//给构造方法传递一个比较器
TreeSet<WuGui> wuGui=new TreeSet<>(new WuGuiComparator());
wuGuis.add(new WuGui(1000));
wuGuis.add(new WuGui(800));
wuGuis.add(new WuGui(810));
for(WuGui wuGui:wuGuis){
System.out.println(wuGui);
}
//编写一个比较器的第二种方法:匿名内部类(这个类没有名字,直接new接口),这样可以不用创建类
TreeSet<WuGui> wuGui=new TreeSet<>(new Comparator<WuGui>(){
public int compare(WuGui w1,WuGui w2){
return w1.age-w2.age;
}
});
}
//当类中实现没有泛型时:
public class User implements Comparator{ }
public void test(){
Comparator com = new Comparator(){
//按照年龄从小到大排序
public int compare(Object o1,Object o2){
if(o1 instanceof User && o2 instanceof User){ //判断比较的两个对象类型是否相同
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge()); //根据年龄大小进行排序
}else {
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
//当传入参数时使用比较器comparator进行定制排序
TreeSet set = new TreeSet(com);
set.add(new User(Tom,12));
//当两个对象的年龄相同时,会将其中第一个出现的存入集合中
set.add(new User(Jerry,32));
set.add(new User(Jim,32));
//遍历
Iterator iterator = set.iterator();
while(iterator.hashNext()){
System.out.println(iterator.next());
}
}
TreeSet
的有参构造方法:
比较器赋值给了comparator
此时调用put
方法比较时comparator
不为空
最终结论:放到TreeSet
或者TreeMap
集合key
部分的元素要想做到排序,有两种方式:一、放在集合中的元素实现java.util.Comparable
接口。二、在构造TreeSet
或者TreeMap
集合的时候给它传一个比较器对象。
Comparable
和Comparator
怎么选择?
答:当比较规则不会发生改变时(比如String,Integer等实现了comparable接口比较规则无法改变),或者比较规则只有一个的时候,建议实现Comparator
接口。当比较规则有多个,并且需要多个比较规则之间频繁切换时,建议使用Comparator
接口。
往HashSet
和TreeSet
里存放数据实际上是存放到了HashMap
和TreeMap
的key
部分。