学科分类
目录
Java基础

TreeSet集合

TreeSet是Set接口的另一个实现类,它内部采用平衡二叉树来存储元素,这样的结构可以保证TreeSet集合中没有重复的元素,并且可以对元素进行排序。所谓二叉树就是说每个节点最多有两个子节点的有序树,每个节点及其子节点组成的树称为子树,通常左侧的子节点称为“左子树”,右侧的节点称为“右子树”,其中左子树上的元素小于它的根结点,而右子树上的元素大于它的根结点。二叉树中元素的存储结构如图1所示。

图1 二叉树的存储结构

在图1所示的二叉树中,同一层的元素,左边的元素总是小于右边的元素。为了使初学者更好的理解TreeSet集合中二叉树存放元素的原理,接下来就分析一下二叉树中元素的存储过程。当二叉树中存入新元素时,新元素首先会与第1个元素(最顶层元素)进行比较,如果小于第1个元素就执行左边的分支,继续和该分支的子元素进行比较;如果大于第1个元素就执行右边的分支,继续和该分支的子元素进行比较。如此往复,直到与最后一个元素进行比较时,如果新元素小于最后一个元素就将其放在最后一个元素的左子树上,如果大于最后一个元素就将其放在最后一个元素的右子树上。

上面通过文字描述的方式对二叉树的存储原理进行了讲解,接下来通过一个具体的图例来演示二叉树的存储过程。假设向集合中存入8个元素,依次为13、8、17、17、1、11、15、25,如果以二叉树的方式来存储,在集合中的存储结构会形成一个树状结构,如图2所示。

图2 二叉树

从图2可以看出,在向TreeSet集合依次存入元素时,首先将第1个存入的元素放在二叉树的最顶端,之后存入的元素与第一个元素比较,如果小于第一个元素就将该元素放左子树上,如果大于第1个元素,就将该元素放在右子树上,依次类推,按照左子树元素小于右子树元素的顺序进行排序。当二叉树中已经存入一个17的元素时,再向集合中存入一个为17的元素时,TreeSet会将重复的元素去掉。

针对TreeSet集合存储元素的特殊性,TreeSet在继承Set接口的基础上实现了一些特有的方法,如表1所示。

表1 TreeSet集合的特有方法

方法声明 功能描述
Object first() 返回TreeSet集合的首个元素
Object last() 返回TreeSet集合的最后一个元素
Object lower(Object o) 返回TreeSet集合中小于给定元素的最大元素,如果没有返回null
Object floor(Object o) 返回TreeSet集合中小于或等于给定元素的最大元素,如果没有返回null
Object higher(Object o) 返回TreeSet集合中大于给定元素的最小元素,如果没有返回null
Object ceiling(Object o) 返回TreeSet集合中大于或等于给定元素的最小元素,如果没有返回null
Object pollFirst() 移除并返回集合的第一个元素
Object pollLast() 移除并返回集合的最后一个元素

了解了TreeSet集合存储元素的原理和一些常用元素操作方法后,接下来通过一个案例来演示TreeSet集合中常用方法的使用,如文件1所示。

文件1 Example11.java

 1    import java.util.TreeSet;
 2    public class Example11 {
 3        public static void main(String[] args) {
 4            // 创建TreeSet集合
 5             TreeSet ts = new TreeSet();     
 6            // 1、向TreeSet集合中添加元素
 7            ts.add(3);
 8            ts.add(9);
 9            ts.add(1);
 10            ts.add(21);
 11            System.out.println("创建的TreeSet集合为:"+ts);
 12            // 2、获取首尾元素
 13            System.out.println("TreeSet集合首元素为:"+ts.first());
 14            System.out.println("TreeSet集合尾部元素为:"+ts.last());
 15            // 3、比较并获取元素
 16            System.out.println("集合中小于或等于9的最大的一个元素为:"
 17                                   +ts.floor(9)); 
 18            System.out.println("集合中大于10的最小的一个元素为:"+ts.higher(10));
 19            System.out.println("集合中大于100的最小的一个元素为:"
 20                                   +ts.higher(100));
 21            // 4、删除元素
 22            Object first = ts.pollFirst();
 23            System.out.println("删除的第一个元素是:"+first);
 24            System.out.println("删除第一个元素后TreeSet集合变为:"+ts);
 25        }
 26    }

运行结果如图3所示。

图3 运行结果

从图3可以看出,使用TreeSet集合的方法正确完成了集合元素的操作。另外从输出结果也可以看出,向TreeSet集合添加元素时,不论元素的添加顺序如何,这些元素都能够按照一定的顺序进行排列,其原因是每次向TreeSet集合中存入一个元素时,就会将该元素与其他元素进行比较,最后将它插入到有序的对象序列中。集合中的元素在进行比较时,都会调用compareTo()方法,该方法是Comparable接口中定义的,因此要想对集合中的元素进行排序,就必须实现Comparable接口。Java中大部分的类都实现了Comparable接口,并默认实现了接口中的CompareTo()方法,如Integer、Double和String等。

在实际开发中,除了会向TreeSet集合中存储一些Java中默认的类型数据外,还会存储一些用户自定义的类型数据,如Student类型数据、Teacher类型数据等。由于这些自定义类型的数据没有实现Comparable接口,因此也就无法直接在TreeSet集合中进行排序操作。为了解决这个问题,Java提供了两种TreeSet的排序规则,分别为:自然排序和定制排序。在默认情况下,TreeSet集合都是采用自然排序,接下来将对这两种排序规则进行详细讲解。

1.自然排序

自然排序要求向TreeSet集合中存储的元素所在类必须实现Comparable接口,并重写compareTo()方法,然后TreeSet集合就会对该类型元素使用compareTo()方法进行比较,并默认进行升序排序。

接下来,就以自定义的Teacher类为例,来演示TreeSet集合中自然排序的使用,如文件2所示。

文件2 Example12.java

 1    import java.util.TreeSet;
 2    // 定义Teacher类实现Comparable接口
 3    class Teacher implements Comparable { 
 4        String name;
 5        int age;
 6        public Teacher(String name, int age) {
 7            this.name = name;
 8            this.age = age;
 9        }
 10        public String toString() {            
 11            return name + ":" + age;
 12        }
 13        //重写Comparable接口的compareTo()方法
 14        public int compareTo(Object obj){
 15            Teacher s = (Teacher) obj;    
 16             // 定义比较方式,先比较年龄age,再比较名称name     
 17            if(this.age -s.age > 0) {        
 18                    return 1;
 19            }
 20            if(this.age -s.age == 0) {        
 21                return this.name.compareTo(s.name);    
 22            }
 23            return -1;
 24        }
 25    }
 26    public class Example12 {
 27        public static void main(String[] args) {
 28            TreeSet ts = new TreeSet();               
 29            ts.add(new Teacher("Jack",19));           
 30            ts.add(new Teacher("Rose",18));
 31            ts.add(new Teacher("Tom", 19));
 32            ts.add(new Teacher("Rose",18));
 33            System.out.println(ts);
 34       }
 35    }

运行结果如图4所示。

图4 运行结果

文件2中,Teacher类实现了Comparable接口,并重写了compareTo()方法。在compareTo()方法中,首先先针对age值进行比较,根据比较结果返回-1和1,当age相同时,再对name进行比较。因此,从运行结果可以看出,教师Teacher对象首先按照年龄升序排序,年龄相同时会按照姓名进行升序排序,并且TreeSet集合会将重复的元素去掉。

2.定制排序

有时候,用户自定义的类型数据所在的类没有实现Comparable接口或者对于实现了Comparable接口的类而不想按照定义的compareTo()方法进行排序。例如,希望存储在TreeSet集合中的字符串可以按照长度而不是英文字母的顺序来进行排序,这时,可以通过在创建TreeSet集合时就自定义一个比较器来对元素进行定制排序。接下来通过一个案例来实现TreeSet集合中字符串按照长度进行定制排序,如文件3所示。

文件3 Example13.java

 1    import java.util.Comparator;
 2    import java.util.TreeSet;
 3    // 定义比较器实现Comparator接口
 4    class MyComparator implements Comparator {  
 5        public int compare(Object obj1, Object obj2) {  // 定制排序方式
 6            String s1 = (String) obj1;
 7            String s2 = (String) obj2;
 8            int temp = s1.length() - s2.length();
 9            return temp;
 10        }
 11    }
 12    public class Example13 {
 13        public static void main(String[] args) {
 14            // 1、创建集合时,传入Comparator接口实现定制排序规则
 15            TreeSet ts = new TreeSet(new MyComparator());
 16            ts.add("Jack");
 17            ts.add("Helena");
 18            ts.add("Eve");
 19            System.out.println(ts);
 20            // 2、创建集合时,使用Lambda表达式定制排序规则
 21            TreeSet ts2 = new TreeSet((obj1, obj2) -> {
 22                String s1 = (String) obj1;
 23                String s2 = (String) obj2;
 24                return s1.length() - s2.length();
 25            });
 26            ts2.add("Jack");
 27            ts2.add("Helena");
 28            ts2.add("Eve");
 29            System.out.println(ts2);
 30        }
 31    }

运行结果如图5所示。

图5 运行结果

文件3中,使用了TreeSet集合的public TreeSet(Comparator<? super E> comparator)有参构造方法,分别传入Comparable接口实现类MyComparator以及Lambda表达式两种参数方式创建了定制序规则的TreeSet集合,当向集合中添加元素时,TreeSet集合就会按照定制的排序规则进行比较,从而使存入TreeSet集合中的字符串按照长度进行排序。

注意:

在使用TreeSet集合存储数据时,TreeSet集合会对存入元素进行比较排序,所以为了保证程序的正常运行,一定要保证存入TreeSet集合中的元素是同一种数据类型。

点击此处
隐藏目录