Java——(四)Collection之Set集合TreeSet类

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

TreeSet类

  TreeSet是SortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合

元素处于排序状态。与HashSet集合相比,TreeSet还提供了如下几个额外的方法。

1)Comparator comparator():

2)Object first():

3)Object last():

4)Object lower(Object o):

5)Object higher(Object 0):

6)SortedSet subSet(fromElemnet, toElement):

7)SortedSet headSet(toElement):返回此Set的子集,由小于toElement的元素组成。

8)SortedSet tailSet(fromElement):返回此Set的子集,由大于或等于fromElement的元素组成。

下面出现测试了TreeSet的通用用法:

 1 import java.util.TreeSet;
 2
 3
 4 public class TreeSetTest {
 5
 6     public static void main(String[] args) {
 7
 8         TreeSet numsSet = new TreeSet<>();
 9         //向TreeSet中添加四个Integer对象
10         numsSet.add(5);
11         numsSet.add(2);
12         numsSet.add(10);
13         numsSet.add(-2);
14         //输出集合元素,看到集合元素已经处于排序状态
15         System.out.println(numsSet);
16         //输出集合里的第一个元素
17         System.out.println(numsSet.first());
18         //输出集合里的最后一个元素
19         System.out.println(numsSet.last());
20         //返回小于4的子集,不包含4
21         System.out.println(numsSet.headSet(4));
22         //返回大于5的子集,如果、set中包含5,子集中也包含5
23         System.out.println(numsSet.tailSet(5));
24         //返回大于等于-3、小于4的子集
25         System.out.println(numsSet.subSet(-3, 4));
26     }
27
28 }

运行结果:

[-2, 2, 5, 10]
-2
10
[-2, 2]
[5, 10]
[-2, 2]

  与HashSet集合采用hash算法来决定因素的存储位置不同,TreeSet采用红黑树的数据结构

来存储集合元素。TreeSet支持两种排序方法:自然排序和定制排序。默认情况下采用自然排序。

1.自然排序

  TreeSet会调用集合元素的Comparable接口中的compareTo(Object obj)方法来比较元素

之间的大小关系,然后将集合元素按升序排列。当一个对象调用该方法与另一个对象进行比较时,

例如obj1.compareTo(obj2),如果该方法返回0,则表明这两个对象相等;如果该方法返回一个

正数,则表明obj1大于obj2;如果该方法返回一个负数,则表明obj1小于obj2.

下面是实现了Comparable接口的常用类:

BigDecimal、BigInteger以及所有的数值型对应的包装类:按它们对应的数值大小进行比较。

  • Character:按字符的UNICODE值进行比较。
  • Boolean:true对应的包装类实例大于false对应的包装类实例。
  • String:按字符串中的字符UNICODE值进行比较。
  • Date、Time:后面的时间、日期比前面的时间、日期大。

  如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则则程

序将会抛出异常。下面程序师范了这个错误。

 1 import java.util.TreeSet;
 2
 3 class Err{
 4
 5 }
 6
 7 public class TreeSetErrorTest {
 8
 9     public static void main(String[] args) {
10
11         TreeSet tSet =new TreeSet<>();
12         //向TreeSet集合中添加两个Err对象
13         tSet.add(new Err());
14         tSet.add(new Err());
15     }
16
17 }

运行结果:

1 Exception in thread "main" java.lang.ClassCastException: Err cannot be cast to java.lang.Comparable
2     at java.util.TreeMap.compare(Unknown Source)
3     at java.util.TreeMap.put(Unknown Source)
4     at java.util.TreeSet.add(Unknown Source)
5     at TreeSetErrorTest.main(TreeSetErrorTest.java:13)

  上面程序试图向TreeSet集合中添加两个Err对象,添加第一个对象时,TreeSet里没有任何

因素,所以不会出现任何问题;当添加第二个Err对象时,TreeSet就会调用该对象的

ComparaTo(Object obj)方法与集合中的其他因素进行比较——如果其对应的类没有实现

Comparable接口则会引发ClassCastException异常。因此。上面程序将会在添加第二个对象的

时候引发该异常。当试图从TreeSet中取出因素时,由于第一个元素没有实现Comparable接口,

因此也会引发ClassCastException异常。

  大部分类在实现compareTo(Object obj)方法时,都需要将比较对象obj强制转换成相同类型

,因为只有相同类型的两个实例次啊会比较大小。当试图把一个对象添加到TreeSet集合时,

TreeSet会调用该对象的compareTo(Object obj)方法与集合中的其他因素进行比较——这就是要

求集合中的其他因素与该因素时同一个类的实例。也就是说,向TreeSet中添加的应该是同一个类

的对象,否则也会引发ClassCastException异常。

下面程序示范了这个错误。

 1 import java.util.Date;
 2 import java.util.TreeSet;
 3
 4
 5 public class TreeSetErrorTest2 {
 6
 7     public static void main(String[] args) {
 8
 9         TreeSet tSet = new TreeSet<>();
10         tSet.add(new String("黑马程序员"));
11         tSet.add(new Date());
12     }
13
14 }

运行结果:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Date
    at java.util.Date.compareTo(Unknown Source)
    at java.util.TreeMap.put(Unknown Source)
    at java.util.TreeSet.add(Unknown Source)
    at TreeSetErrorTest2.main(TreeSetErrorTest2.java:11)

上面程序先向TreeSet集合中添加了一个字符串对象,这个操作完全正确。当添加第二个Date对

象时,TreeSet就会调用该对象的compareTo(Object obj)方法与集合中的其他因素进行比较

——Date对象的compareTo(Object obj)方法无法与字符串对象比较大小,所以上面程序将在

添加Date对象时引发异常。

  如果是向TreeSet中添加的对象是程序员自定义类的对象,则可以向TreeSet中添加多种类型

的对象,前提是用户自定义类实现了Comparable接口,实现该接口实现的compareTo(Object obj)

方法没有进行强制类型转换。但当试图取出TreeSet里的集合数据时,不同类型的元素依然会发生

ClassCastException异常。

  当把一个对象加入TreeSet集合中时,TreeSet调用该对象的compareTo(Object obj)方法与

容器中的其他对象比较大小,然后根据红黑树结构找到它的存储位置。如果两个对象通过

compareTo(Object obj)方法比较相等,新对象将无法添加到TreeSet集合中。他判断两个对象是否

相等的唯一标准是:两个对象通过compareTo(Object obj)方法比较是否返回0——如果通过

compareTo(Object obj)方法比较返回0,TreeSet则会认为它们相等;否则就认为它们不相等。

示例:

 1 import java.util.TreeSet;
 2
 3
 4 class Z implements Comparable{
 5     int age;
 6     public Z(int age){
 7         this.age = age;
 8     }
 9     //重写equals()方法
10     @Override
11     public boolean equals(Object obj) {
12
13         return true;
14     }
15     @Override
16     public int compareTo(Object obj) {
17
18         return 1;
19     }
20 }
21 public class TreeSetTest2 {
22
23     public static void main(String[] args) {
24
25         TreeSet set = new TreeSet<>();
26         Z z1 = new Z(6);
27         set.add(z1);
28         //输出true,表明添加成功
29         System.out.println(set.add(z1));
30         //输出set集合,将看到有两个变量
31         System.out.println(set);
32         //修改set集合的第一个元素的age变量
33         ((Z)(set.first())).age = 9;
34         //输出set集合的最后一个元素的age变量,将看到也变成了9
35         System.out.println(((Z)(set.first())).age);
36     }
37
38 }

运行结果:

true
[[email protected], [email protected]]
9

  程序中把同一对象再次添加到TreeSet集合中,因为z1对象的compareTo(Object obj)方法

总是返回1,虽然它的equals()方法总是返回true,但TreeSet会认为z1对象和它自己也不相等,

因此TreeSet可以添加两个z1对象。下图显示了TreeSet及Z对象在内存中的存储示意图。

从图中可以看到TreeSet对象保存的两个对象,实际上是同一元素。

如果把重写的compareTo(Object obj)方法中的返回值改为1,则运行结果如下:

false
[[email protected]]
9

如果向TreeSet中添加一个可变对象后,并且后面程序修改了该可变对象的Field,这将导致它与

其他对象的大小顺序发生了改变,但TreeSet不会再次调整它们的顺序,甚至可能导致TreeSet

中保存的这两个对象通过compareTo(Object obj)方法比较返回0.下面程序演示了这种情况。

 1 import java.util.TreeSet;
 2
 3 class M implements Comparable {
 4
 5     int count;
 6
 7     public M(int count) {
 8         this.count = count;
 9     }
10
11     @Override
12     public String toString() {
13
14         return "M[count:" + count + "]";
15     }
16     @Override
17     public boolean equals(Object obj) {
18
19         if (this == obj) {
20             return true;
21         }
22         if (obj != null && obj.getClass() == M.class) {
23             M m = (M)obj;
24             if (m.count == this.count) {
25                 return true;
26             }
27         }
28         return false;
29     }
30     //重写compareTo()方法,根据count来比较大小
31     @Override
32     public int compareTo(Object obj) {
33
34         M m = (M) obj;
35         return count > m.count ? 1 : count < m.count ? -1 : 0;
36     }
37
38 }
39
40 public class TreeSetTest3 {
41
42     public static void main(String[] args) {
43
44         TreeSet tsSet = new TreeSet<>();
45         tsSet.add(new M(5));
46         tsSet.add(new M(-3));
47         tsSet.add(new M(9));
48         tsSet.add(new M(-2));
49         //打印TreeSet集合,集合元素是有序排列的
50         System.out.println(tsSet);
51         //取出第一个元素的count值
52         M firstM = (M)tsSet.first();
53         //对第一个元素赋值
54         firstM.count = 20;
55         //取出最后一个元素
56         M lastM = (M)tsSet.last();
57         //对最后一个元素的count值赋值,与第二个元素的count值相同
58         lastM.count = -2;
59         //输出可以看到TreeSet集合里的元素处于无序状态,且有重复元素
60         System.out.println(tsSet);
61         //删除Field被改变的元素,删除失败
62         System.out.println(tsSet.remove(new M(-2)));
63         System.out.println(tsSet);
64         //删除field没有改变的元素,删除成功
65         System.out.println(tsSet.remove(new M(5)));
66         System.out.println(tsSet);
67     }
68
69 }

运行结果:

[M[count:-3], M[count:-2], M[count:5], M[count:9]]
[M[count:20], M[count:-2], M[count:5], M[count:-2]]
false
[M[count:20], M[count:-2], M[count:5], M[count:-2]]
true
[M[count:20], M[count:-2], M[count:-2]]

上面程序中的M对象对应的类正常重写了equals()方法和compareTo()方法,这两个方法都以M

对象的count实例变量作为判断的依据。当改变TreeSet集合了可变元素的Field,再试图删除该对

象时,TreeSet会删除失败,所以删除count为-2的M对象时,没有元素被删除;当删除count为5

的M对象时,可以看到元素被删除,这表明TreeSet可以删除没有被修改的Field,且不与其他被修

改Field的对象重复的对象。

2.定制排序

  TreeSet的自然排序是根据集合元素的大小,TreeSet将它们一升序排序。如果需要实现定制

排序,例如以降序排列,则可以通过Comparator接口的帮助。该接口里包含了一个int compare

(T o1, T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表明o1大于o2;

如果该方法返回0,则表明o1等于o2;如果该方法返回负整数,则表明o1小于o2。

  实现定制排序,则需要在创建TreeSet集合对象时,提供一个Comparator对象与该TreeSet

集合关联,由该Comparator对象负责集合元素的排序。

 1 import java.util.Comparator;
 2 import java.util.TreeSet;
 3
 4 class C {
 5     int age;
 6
 7     public C(int age) {
 8         this.age = age;
 9     }
10
11     @Override
12     public String toString() {
13
14         return "C[age:" + age + "]";
15     }
16 }
17
18 public class TreeSetTest4 {
19
20     public static void main(String[] args) {
21
22         TreeSet tSet = new TreeSet<>(new Comparator() {
23             @Override
24             public int compare(Object o1, Object o2) {
25                 C c1 = (C) o1;
26                 C c2 = (C) o2;
27                 return c1.age > c2.age ? -1 : c1.age < c2.age ? 1 : 0;
28             }
29         });
30         tSet.add(new C(5));
31         tSet.add(new C(-3));
32         tSet.add(new C(9));
33         System.out.println(tSet);
34     }
35
36 }

运行结果:

[C[age:9], C[age:5], C[age:-3]]
时间: 2024-10-15 13:59:34

Java——(四)Collection之Set集合TreeSet类的相关文章

Java之Collection接口(集合)

集合概述 集合到底是什么呢?集合:集合是java中提供的一种容器,可以用来存储多个数据 集合和数组既然都是容器,它们有啥区别呢? 区别1: 数组的长度是固定的. 集合的长度是可变的. 区别2:  数组中存储的是同一类型的元素,可以存储基本数据类型值,也可以存储引用类型: 集合存储的都是对象.而且对象的类型可以不一致.在开发中一般当对象多的时候,使用集合进行存储. 学习集合的目标 会使用集合存储数据 会遍历集合,把数据取出来 掌握每种集合的特性 学习集合的方式 学习顶层:学习顶层接口或者抽象类中共

Java中Collection、Map常用实现类研究分析

接口/实现类 描述 key是否可为null 为null是否报错 key是否重复 key重复是否报错 key是否和添加一致 是否线程安全 List 一组元素的集合 ArrayList 基于数组存储,读取快 是 否 是 否 是 否 LinkedList 基于双向链表存储,插入快 是 否 是 否 是 否 Vector 基于数组存储,效率较ArrayList慢 是 否 是 否 是 是 Map HashMap 是 否 否 否 否 否 TreeMap 基于红黑树实现 否 是 否 否 否 否 Concurre

Java中Collection和Collections的区别

1.java.util.Collection 是一个集合接口(集合类的一个顶级接口).它提供了对集合对象进行基本操作的通用接口方法.Collection接口在Java 类库中有很多具体的实现.Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set. Collection   ├List   │├LinkedList   │├ArrayList   │└Vector   │ └Stack   └Set 2.java.util.Collectio

java类库 collection与collections (转)

http://www.cnblogs.com/dashi/p/3597937.html Java中Collection和Collections的区别 1.java.util.Collection 是一个集合接口(集合类的一个顶级接口).它提供了对集合对象进行基本操作的通用接口方法.Collection接口在Java 类库中有很多具体的实现.Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set. Collection   ├List   │├

Java中Collection和Collections的区别(引用自:http://www.cnblogs.com/dashi/p/3597937.html)

1.java.util.Collection 是一个集合接口(集合类的一个顶级接口).它提供了对集合对象进行基本操作的通用接口方法.Collection接口在Java 类库中有很多具体的实现.Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set. Collection   ├List   │├LinkedList   │├ArrayList   │└Vector   │ └Stack   └Set 2.java.util.Collectio

Java集合框架总结(3)——TreeSet类的排序问题

Java集合框架总结(3)--TreeSet类的排序问题 TreeSet支持两种排序方法:自然排序和定制排序.TreeSet默认采用自然排序. 1.自然排序 TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间大小关系,然后将集合元素按升序排列,这种方式就是自然排序.(比较的前提:两个对象的类型相同). java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该

Java——(五)Collection之List集合、ArrayList和Vector实现类

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.List集合 List集合代表一个元素有序.客重复的集合,集合中每个元素都有其对应的顺序索引.List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素.List集合默认按元素的添加 顺序设置元素的索引,例如第一次添加的元素索引为0,第二次添加的元素索引为1...... 1.List接口和ListIterator接口 List作为Collection接口的子接口,所以可以使用Co

Java集合--TreeSet详细解析

目录 1.构造函数 2.增 3.删 4.比较器 总结 谈到TreeSet的特点,估计大家脑海里想到的都是:有序,不可重复,红黑树,基于Treemap实现,自定义排序等特点.这篇博客帮助大家从源码梳理下TreeSet的知识点. 1.构造函数 TreeSet提供了四种构造器 TreeSet() TreeSet(Collection< ? extends E> c) TreeSet(Comparator< ? super E> comparator) TreeSet(SortedSet&

Java API —— TreeSet类

1.TreeSet类  1)TreeSet类概述 使用元素的自然顺序对元素进行排序 或者根据创建 set 时提供的 Comparator 进行排序 具体取决于使用的构造方法.   2)TreeSet是如何保证元素的排序和唯一性的 底层数据结构是红黑树(红黑树是一种自平衡的二叉树) 例子1: package treesetdemos; import java.util.TreeSet; /** * Created by gao on 15-12-17. */ /* * TreeSet:能够对元素按