TreeSet集合解析

  TreeSet是实现Set接口的实现类。因此它存储的值是惟一的,同时也能够对存储的值进行排序,排序用的是二叉树原理。因此要理解这个类,必须先简单理解一下什么是二叉树。java

二叉树原理简析

  假若有这么一个集合TreeSet<Integer>是[5,11,6,5,23,14]ide

  用二叉树是怎么排序的呢?函数

  二叉树遍历方法比较多,有兴趣本身百度看下吧。这里只须要知道元素是怎么插入到二叉树便可。小的存储在左边(负数),大的存储在右边(正数),相等不存储。this

TreeSet的基本使用

@Test
    public void test() {
        Set<Integer> set = new TreeSet<Integer>();
        set.add(2);
        set.add(22);
        set.add(24);
        set.add(2);
        for (int i : set) {
            System.out.println(i);
        }
    }

 

 输出结果spa

2
22
24

  能够知道,TreeSet集合不只能够保证集合元素的惟一性,还能够排序。code

  若是TreeSet里面存储的是对象呢?会出现什么状况呢?对象

public static void main(String[] args) {
        User u1 = new User("Kevin", 72);
        User u2 = new User("Jack", 22);
        User u3 = new User("Erik", 32);
        User u4 = new User("Blank", 72);
        Set<User> set = new TreeSet<User>();
        set.add(u1);
        set.add(u2);
        set.add(u3);
        set.add(u4);
        for (User u :
                set) {
            System.out.println(u);
        }
    }

 输出结果blog

Exception in thread "main" java.lang.ClassCastException: com.kevin.exercise10.User cannot be cast to java.lang.Comparable
    at java.util.TreeMap.put(TreeMap.java:542)
    at java.util.TreeSet.add(TreeSet.java:238)
    at com.kevin.exercise10.TreeSetTest.main(TreeSetTest.java:23)

  报错了,由于集合里面的是对象,对象不能转换为比较可比较对象。排序

  若是想根据年龄排序,打印出各个对象(toString方法),应该怎么作呢?在API里面搜索一下Comparable,发现是个接口,那么咱们就可让Students类实现Comparable接口方法,这样Students对象就成为了可比较对象了。接口

  User类实现Comparable接口方法:

public class User implements Comparable<User> {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(User u) {
        int num = this.age - u.age;
        return this.age - u.age;
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public User() {

    }
}

  为何是this.age-u.age?this.age表明调用时的对象的age,返回的若是是正数(比u.age大),就存储在右边。返回的是若是是负数(比u.age小),就存储在左边。若是等于0,就不存储。

  这就出问题了,若是两我的不一样名字,一样年龄,this.age - o.age = 0,不就存不进二叉树了吗?

验证一下:

public static void main(String[] args) {
        User u1 = new User("Kevin", 72);
        User u2 = new User("Jack", 22);
        User u3 = new User("Erik", 32);
        User u4 = new User("Blank", 72);
        Set<User> set = new TreeSet<User>();
        set.add(u1);
        set.add(u2);
        set.add(u3);
        set.add(u4);
        for (User u :
                set) {
            System.out.println(u);
        }
    }

输出结果:

User{name='Jack', age=22}
User{name='Erik', age=32}
User{name='Kevin', age=72}

  因此须要改进一下User类的compareTo方法,保证同年龄,可是不一样名字的学生也能存进二叉树。

@Override
    public int compareTo(User u) {
        int num = this.age - u.age;
        return num == 0 ? this.name.compareTo(u.name) : num;
    }

  这样就能够把四个不一样的对象存储进来,而且先按照年龄排序,年龄相同的再按照字符串排序。

输出结果:

User{name='Jack', age=22}
User{name='Erik', age=32}
User{name='Blank', age=72}
User{name='Kevin', age=72}

 

---------------------------------------------------------------------------------------

除了这种方式能够实现排序之外,还有一种方式能够实现排序。

  TreeSet有这么一个构造方法:

  TreeSet(Comparator<? super E> comparator) 构造一个新的,空的树集,根据指定的比较器进行排序。

  Comparator是什么呢?API文档看一下:Interface Comparator<T>,是一个接口,里面有一个要实现的接口方法:int compare(T o1, T o2) 比较其两个参数的顺序。

  例如,咱们要对字符串的长度进行排序,长度相同的安装字符串排序:

public class TreeSetTest2 {
    public static void main(String[] args) {
        TreeSet<String> set = new TreeSet<String>(new SortedByLen());
        set.add("abc");
        set.add("abcd");
        set.add("b");
        set.add("a");
        set.add("abab");
        System.out.println(set);
    }
}

class SortedByLen implements Comparator<String> {
    @Override
    public int compare(String s1, String s2) {
        int num = s1.length() - s2.length();
        return num == 0 ? s1.compareTo(s2) : num;
    }

}

  须要注意的是重写compare方法的o1,o2。o1表明调用的对象,o2表明集合中的对象。两种实现排序方式视状况而用。

(1)天然顺序(Comparable)

  • TreeSet类的add()方法中会把存入的对象提高为Comparable类型
  • 调用对象的compareTo()方法和集合中的对象比较
  • 根据compareTo()方法返回的结果进行存储

(2)比较器顺序(Comparator)

  • 建立TreeSet的时候能够指定一个Comparator
  • 若是传入了Comparator的子类对象,那么TreeSet就会按照比较器中的顺序排序
  • 调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数

(3)两种方式的区别

  • TreeSet构造函数什么都不传,默认按照类中Comparable的顺序(没有就报错ClassCastException)
  • TreeSet若是传入Comparator,就优先按照Comparator

  若是不想保证元素的惟一性,改一下compare方法就能够了,永远不要让它返回0。

 

 

 

 

原文参考【https://www.javazhiyin.com/

相关文章
相关标签/搜索