TreeSet
从名字上可以看出,此集合的实现和树结构有关。
与HashSet集合类似,TreeSet基于TreeMap来实现的,其底层结构为红黑树(特殊的二叉查找树,即在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black)。
与HashSet不同的是,TreeSet具有排序功能,分为自然排序(123456)和自定义排序两类,默认是自然排序;在程序中,我们可以按照任意顺序将元素插入到集合中,等到遍历时TreeSet会按照一定顺序输出–倒序或者升序。
特点
- 对插入的元素进行排序,是一个有序的集合(这是主要与HashSet的区别);
- 底层使用红黑树结构,而不是哈希表结构;
- 允许插入Null值;
- 不允许插入重复元素;
- 线程不安全。
TreeSet基本操作
1 | public class TreeSetTest { |
TreeSet元素排序
在前面,我们提到了TreeSet是一个有序集合,可以对集合元素排序,其中分为自然排序和自定义排序,那么这两种方式如何实现呢?
情况1
首先,我们通过JDK提供的对象来展示,我们使用String、Integer:
1 | public class TreeSetTest { |
结果
1 | 字母顺序:[a, b, d, z] |
情况2 - 自定义对象
接下来,我们自定义对象,看能否实现:
1 | public class App{ |
结果
1 | 抛出异常:提示App不能转换为Comparable对象: |
分析
为什么会报错呢?
1 | compare(key, key); // type (and possibly null) check |
通过查看源码发现,在TreeSet调用add方法时,会调用到底层TreeMap的put方法,在put方法中会调用到compare(key, key)方法,进行key大小的比较。
在比较的时候,会将传入的key进行类型强转,所以当我们自定义的App类进行比较的时候,自然就会抛出异常,因为App类并没有实现Comparable接口。
将App实现Comparable接口,再做比较:
1 | public class App implements Comparable<App>{ |
结果
1 | TreeSet集合顺序为:[App{name='my', age=15}, App{name='name', age=25}, App{name='hello', age=10}, App{name='world', age=20}] |
情况3 - 实现Comparetor<T>接口
此外,还有另一种方式,那就是实现Comparetor
1 | //自定义App类的比较器: |
此时,App不用在实现Comparerable接口了,单纯的定义一个类即可;
1 | public class App{ |
结果
1 | TreeSet集合顺序为:[App{name='hello', age=10}, App{name='my', age=15}, App{name='world', age=20}, App{name='name', age=25}] |
Reference
- Java集合–Set(基础) - https://www.jianshu.com/p/b48c47a42916