Java深入了解TreeSet

Java中的TreeSet是Set的一个子类,TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一。
那TreeSet为什么能保证元素唯一,它是怎样排序的呢?先看一段代码:

public static void demo() {
        TreeSet<Person> ts = new TreeSet<>();
        ts.add(new Person("张三", 23));
        ts.add(new Person("李四", 13));
        ts.add(new Person("周七", 13));
        ts.add(new Person("王五", 43));
        ts.add(new Person("赵六", 33));

        System.out.println(ts);
    }

执行结果:

出错,会抛出一个异常:java.lang.ClassCastException
显然是出现了类型转换异常。原因在于我们需要告诉TreeSet如何来进行比较元素,如果不指定,就会抛出这个异常

如何解决:
如何指定比较的规则,需要在自定义类(Person)中实现```Comparable```接口,并重写接口中的compareTo方法

public class Person implements Comparable<Person> {
    private String name;
    private int age;
    ...
    public int compareTo(Person o) {
        return 0;                //当compareTo方法返回0的时候集合中只有一个元素
        return 1;                //当compareTo方法返回正数的时候集合会怎么存就怎么取
        return -1;                //当compareTo方法返回负数的时候集合会倒序存储
    }
}

为什么返回0,只会存一个元素,返回-1会倒序存储,返回1会怎么存就怎么取呢?原因在于TreeSet底层其实是一个二叉树机构,且每插入一个新元素(第一个除外)都会调用```compareTo()```方法去和上一个插入的元素作比较,并按二叉树的结构进行排列。
1. 如果将```compareTo()```返回值写死为0,元素值每次比较,都认为是相同的元素,这时就不再向TreeSet中插入除第一个外的新元素。所以TreeSet中就只存在插入的第一个元素。
2. 如果将```compareTo()```返回值写死为1,元素值每次比较,都认为新插入的元素比上一个元素大,于是二叉树存储时,会存在根的右侧,读取时就是正序排列的。
3. 如果将```compareTo()```返回值写死为-1,元素值每次比较,都认为新插入的元素比上一个元素小,于是二叉树存储时,会存在根的左侧,读取时就是倒序序排列的。

利用上述规则,我们就可以按照年龄来排序了。代码如图

public int compareTo(Person o) {
        int num = this.age - o.age;                //年龄是比较的主要条件
        return num == 0 ? this.name.compareTo(o.name) : num;//姓名是比较的次要条件
    }

按照姓名排序(依据Unicode编码大小),代码如下:

public int compareTo(Person o) {
        int num = this.name.compareTo(o.name);        //姓名是主要条件
        return num == 0 ? this.age - o.age : num;    //年龄是次要条件
    }

按照姓名长度排序,代码如下:

public int compareTo(Person o) {
        int length = this.name.length() - o.name.length();                //比较长度为主要条件
        int num = length == 0 ? this.name.compareTo(o.name) : length;    //比较内容为次要条件
        return num == 0 ? this.age - o.age : num;                        //比较年龄为次要条件
    }

以上是TreeSet如何比较自定义对象,接下来我们再来看一下TreeSet如何利用比较器比较元素。

需求:现在要制定TreeSet中按照String长度比较String。

//定义一个类,实现Comparator接口,并重写compare()方法,
class CompareByLen /*extends Object*/ implements Comparator<String> {

    @Override
    public int compare(String s1, String s2) {        //按照字符串的长度比较
        int num = s1.length() - s2.length();        //长度为主要条件
        return num == 0 ? s1.compareTo(s2) : num;    //内容为次要条件
    }
}
    public static void demo4() {

        //需求:将字符串按照长度排序
        TreeSet<String> ts = new TreeSet<>(new CompareByLen());        //Comparator c = new CompareByLen();
        ts.add("aaaaaaaa");
        ts.add("z");
        ts.add("wc");
        ts.add("nba");
        ts.add("cba");

        System.out.println(ts);
    }

总结

  1. 特点

    • TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列
  2. 使用方式
    • a.自然顺序(Comparable)

      • TreeSet类的add()方法中会把存入的对象提升为Comparable类型
      • 调用对象的compareTo()方法和集合中的对象比较
      • 根据compareTo()方法返回的结果进行存储
    • b.比较器顺序(Comparator)
      • 创建TreeSet的时候可以制定 一个Comparator
      • 如果传入了Comparator的子类对象, 那么TreeSet就会按照比较器中的顺序排序
      • add()方法内部会自动调用Comparator接口中compare()方法排序
      • 调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
    • c.两种方式的区别
      • TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错ClassCastException)
      • TreeSet如果传入Comparator, 就优先按照Comparator

?

时间: 2024-08-04 04:25:43

Java深入了解TreeSet的相关文章

java HashSet 与 TreeSet

Set接口并没有对Collection接口进行扩充,只是比Collection接口要求更加严格了,不能增加重复的元素,并且采用散列的储存方式,所以没有顺序 import java.util.Set; import java.util.HashSet; import java.util.TreeSet; public class Test { public static void main(String[] args) { //Set allSet = new HashSet();//@结果 1

Java——HashSet和TreeSet的区别

HashSetHashSet有以下特点? 不能保证元素的排列顺序,顺序有可能发生变化? 不是同步的? 集合元素可以是null,但只能放入一个null当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置.简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等注意,如果要把一个

java 17 -7 TreeSet元素排序规则的案例

TreeSet:能够对元素按照某种规则进行排序. 排序有两种方式 A:自然排序 B:比较器排序 TreeSet集合的特点:排序和唯一 通过观察TreeSet的add()方法,我们知道最终要看TreeMap的put()方法. 1 package zl_TreeSet; 2 3 import java.util.TreeSet; 4 public class TreeSetDemo { 5 6 public static void main(String[] args) { 7 //创建TreeSe

java 集合框架(TreeSet操作,自动对数据进行排序,重写CompareTo方法)

/*TreeSet * treeSet存入数据后自动调用元素的compareTo(Object obj) 方法,自动对数据进行排序 * 所以输出的数据是经过排序的数据 * 注:compareTo方法返回值有:负数,零,正数.分别表示小于,等于,大于 * 对于存入自定义的对象元素,要重写元素的compareTo(Object obj)方法 * 元素定义时,需要实现Comparable接口 * */ 1 import java.util.Iterator; 2 import java.util.Tr

java集合类分析-Treeset

//从下面的treeset的源代码可以看出它基本上就是treeMap的一层包装,因此这里过多的介绍,详细可以看treeMap的具体的实现//实现了NavigableSet.以及set接口 其中NavigableSet实现的是sortSet接口 public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable { ///这里就

java 集合框架-TreeSet练习

import java.util.*; /* 练习:按照字符串长度排序. 字符串本身具备比较性,但是它的比较方式不是所需要的.这时就只能使用比较器 注意:在方式二中: int num=new Integer(str1.length()).compareTo(new Integer(str2.length())); 此时num的值有三种可能.正数.负数.零.千万不要写下面的代码: if(num==1) return 1; num不一定等于1.我们在函数中返回的0,1,-1只是代表三种情况:零,正数

java 集合框架-TreeSet

/* TreeSet可以对Set集合中的对象进行排序: 原理,TreeSet在底层调用了Comparable接口的方法compareTo(). 所以,在使用TreeSet存储自定义对象时,要想实现自定义类对象的自然排序,需要类实现Comparable接口,并重写覆盖接口唯一的方法compareTo(). 需求: 往TreeSet集合中存储自定义对象学生. 想按照学生的年龄进行排序. 记住:排序时,当主要条件相同时,一定判断一下次要条件. 在本例中,出现了两个同学的年龄是一样的,如果不进行判断次要

java学习之treeset

Set:无序,不可以重复元素 |--HashSet:数据结构是哈希表,线程是非同步的. 保证元素唯一性的原理:判断元素的hashcode值是否相同 如果相同,还会继续判断元素的equals方法,是否为true |--TreeSet:可以对Set集合中的元素进行排序 注意:排序时,当主要条件相同时,一定要判断一下次要条件(否则会被认为是同一个对象而不被加入) 1 import java.util.Iterator; 2 import java.util.TreeSet; 3 4 /** 5 * 需

死磕 java集合之TreeSet源码分析

问题 (1)TreeSet真的是使用TreeMap来存储元素的吗? (2)TreeSet是有序的吗? (3)TreeSet和LinkedHashSet有何不同? 简介 TreeSet底层是采用TreeMap实现的一种Set,所以它是有序的,同样也是非线程安全的. 源码分析 经过前面我们学习HashSet和LinkedHashSet,基本上已经掌握了Set实现的套路了. 所以,也不废话了,直接上源码: package java.util; // TreeSet实现了NavigableSet接口,所