TreeSet的比较器简单的分析和使用(TreeMap同样适用)

一. TreeSet集合的基本介绍

  1. 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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值