Java集合之TreeSet

TreeSet

阅读这篇文章只需5-10分钟。

TreeSet是SortedSet接口的实现类,正如SortedSet名字暗示的,TreeSet可以确保集合元素处于排序状态。与HashSet集合相比,TreeSet还提供了如下几个额外方法:

  • Comparator comparator();如果TreeSet采用了定制排序,则该方法返回定制排序所使用Comparator;如果TreeSet采用了自然排序,则返回null;
  • Object first();返回集合中的第一个元素;
  • Object last();返回集合中的最后一个元素;
  • Object lower(Object o);返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要是TreeSet集合里的元素);
  • Object higher(Object o);返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素不需要是TreeSet集合里的元素);
  • SortedSet subSet(Object fromElement,Object toElement);返回此Set的子集合,返回从fromElement,Object (包含到)toElement(不包含);
  • SortedSet headSet(Object toElement);返回此Set的子集,由小于toElement的元素组成;
  • SortedSet tailSet(Object fromElement);返回此Set的子集,由大于或等于fromElement的元素组成;

示例:

package com.collection;

import java.util.TreeSet;

public class TreeSetTest {

    public static void main(String[] args) {
        TreeSet set = new TreeSet();
        set.add(9);
        set.add(-2);
        set.add(3);
        set.add(10);
        System.out.println(set);//[-2, 5, 9, 10]
        //输出集合中第一个元素
        System.out.println("set.first() = "+set.first());//set.first() = -2
        //输出集合中最后一个元素
        System.out.println("set.last() = "+set.last());//set.last() = 10
        //返回小于4的子集,不包含4
        System.out.println("set.headSet() = "+set.headSet(4));//set.headSet() = [-2, 3]
        //返回大于5的子集,包含5
        System.out.println("set.tailSet() = "+set.tailSet(5));//set.tailSet() = [9, 10]
        //返回大于等于-3,小于4的子集
        System.out.println("set.subSet() = "+set.subSet(-3, 4));//set.subSet() = [-2, 3]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

与HashSet集合采用通过hash算法来决定元素的存储位置不同,TreeSet采用红黑树的数据结构来存储集合元素。
TreeSet支持两只排序方法:自然排序与定制排序。

自然排序:

TreeSet会调用集合元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式是自然排序。
Java提供了一个Comparable接口,该接口定义一个compareTo(Object o)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现该接口的类的对象就可以比较大小。当一个对象调用该方法与另一个对象进行比较时,例如:obj1.compareTo(obj2),如果该方法返回0,则代表这两个对象相等;如果该方法返回一个正整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表明obj1小于obj2。

特别篇:
Java的一些常用类已经实现了Comparable接口,并提供了比较大小的标准。下面是视线了Comparable接口的常用类。
1 BigDecimal、BigInterger以及所有的数值型对应的包装类:按它们对应的数值大小进行比较。
2 Character: 按字符的UNICODE值进行比较。
3 Boolean: true对应的包装示例大于false对应的包装类实例。
4 String: 按字符串中字符的UNICODE值进行比较。
5 Date、Time: 后面的时间、日期比前面的时间、日期大。

问题1:
如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则程序会抛出异常java.lang.ClassCastException。
如下:

public static void main(String[] args) {
    TreeSet t1= new TreeSet();
        t1.add(new Err());
        t1.add(new Err());
        //Exception in thread "main" java.lang.ClassCastException:

    }
    class Err{

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

原因:
TreeSet集合中添加两个Err对象,添加第一个对象时,TreeSet里没有任何元素,所以不会出现任何问题;当添加第二个Err对象时,TreeSet就会调用该对象的compareTo(Object obj)方法与集合中的其他元素进行比较—如果其对应的类没有实现Comparable 接口,则会引发ClassCastException异常。

问题2:
在实现compareTo(Object o)方法时,都需要将被比较对象obj强制类型转换成相同类型,因为只有相同类的两个示例才会比较大小
当试图把一个大对象添加到TreeSet集合时,TreeSet会调用该对象的compareTo(Object o)方法与集合中的其他元素进行比较—这就要求集合中的其他元素与该元素是同一类的示例,否则抛出ClassCaseException异常。

总之一句话,如果希望TreeSet能够正常运行,TreeSet只能添加同一类型对象。

错误示例:

public static void main(String[] args) {
        TreeSet t2 = new TreeSet();
        t2.add("hello");
        t2.add(1);
        //java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

TreeSet判断标准:

TreeSet判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object o)方法比较是否返回0 —如果通过compareTo(Object o)方法比较返回0,TreeSet则会认为它们相等:否则就认为它们不相等。

示例:
我们定义一个类z ,实现compareTo(Object o )方法,重写equals()方法,TreeSet判断两个对象的标准上面已经讲了,它会已两个对象的compareTo(Object o )比较的返回值来判断,为0时,代表两个对象相等;为1时,代表obg1大于obj2对像,否则返回-1。

class z implements Comparable{
    int size;
    public z( int size){
        this.size = size;
    }

    public int compareTo(Object o) {
    //永远不相等
        return 1;
    }

    public boolean equals(Object obj) {
        return true;
    }

}
public static void main(String[] args) {
    TreeSet t3 = new TreeSet();
        z z1 = new z(6);
        t3.add(z1);
        t3.add(z1);
        //TreeSet添加了两个z对象到集合中,并且添加成功了
        System.out.println(t3);
        //输出的结果显示,这两个对象实际是相等的,之所以能添加成功的原因是compareTo 返回了1,这就表示按当前的示例
    无论添加对象z对象到TreeSet集合中,都会成功,因为它们始终不相等(也就是compareTo返回值 为 0//[com.collection.z@15db9742, com.collection.z@15db9742]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

正确示例:

判断两个对象的是否相等的参照标准,下面我们已Size为例;

class z implements Comparable{

    int size;//判断标准
    public z( int size){
        this.size = size;
    }

    public int compareTo(Object o) {
    z z1 = (z)o;//类型转换
    return this.size > z1.size ? 1 :this.size < z1.size ? -1 : 0;
    }

    public boolean equals(Object obj) {
        if(this == obj)
        {
            return true;
        }

        if(obj != null && obj.getClass() == z.class)
        {
            z z1 = (z)obj) ;//类型转换
            return z1.size == this.size;
        }
    return false;   
    }
}

public static void main(String[] args) {
    TreeSet t5 = new TreeSet();
        t5.add(new R1(6));
        t5.add(new R1(6));
        t5.add(new R1(-6));
        t5.add(new R1(1));
        System.out.println(t5);
        //对重复的元素6进行了对比,输出结果显示只有,6、-6、1
        //[com.collection.R1@6d06d69c, com.collection.R1@7852e922, com.collection.R1@4e25154f]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

注意:
与HashSet类似的是,如果TreeSet中包含了可变对象,当可变对象的示例变量被修改时,TreeSet在处理这些对象时将非常复杂,而且容易出错。为了让程序更加健壮,推荐不要修改放入HashSet和TreeSet集合中元素的关键实例变量。

定制排序

TessSet 的自然排序是根据集合元素的大小,TreeSet将它们以升序排列。如果需要实现定制排序,例如以降序排列,则可通过Comparator接口的帮助。该接口里包含一个int compate(T o1,T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表明o1大于o2;如果该方法返回0,则表明o1等于o2;如果该方法返回负整数,则表明o1小于o2。

示例:

class R2 implements Comparable{

    int size;//判断标准
    public R2( int size){
        this.size = size;
    }

    public int compareTo(Object o) {
        R2 r = (R2)o;//类型转换
        return this.size > r.size ? -1 :this.size < r.size ? 1 : 0;
    }

    public boolean equals(Object obj) {
    if(this == obj){
        return true;
    }

    if(obj != null && obj.getClass() == z.class){
        R2 r = (R2)obj;//类型转换
        return r.size == this.size;
    }
    return false;   
    }

}

    public static void main(String[] args) {
        TreeSet t6 = new TreeSet();
        t6.add(new R2(6));
        t6.add(new R2(6));
        t6.add(new R2(-6));
        t6.add(new R2(1));
        System.out.println(t6);
        R2 r1 = (R2) t6.first();
        System.out.println("r1.first() = "+r1.size);//set.first() = 6
        //输出集合中最后一个元素
        R2 r2 = (R2) t6.last();
        System.out.println("r2.last() = "+r2.size);//set.last() = -6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

Things won are done; joy’s soul lies in the doing.
得到即是完结,快乐的精髓在于过程。

猜你喜欢

转载自blog.csdn.net/hi_boy_/article/details/79461859