TreeSet是实现Set接口的实现类。所以它存储的值是唯一的,同时也可以对存储的值进行排序,排序用的是二叉树原理。所以要理解这个类,必须先简单理解一下什么是二叉树。

二叉树原理简析

  假如有这么一个集合TreeSet<Integer>是[5,11,6,5,23,14]

  用二叉树是怎么排序的呢?

  二叉树遍历方法比较多,有兴趣自己百度看下吧。这里只需要知道元素是怎么插入到二叉树即可。小的存储在左边(负数),大的存储在右边(正数),相等不存储。

TreeSet的基本使用

@Test
public void test() {
Set<Integer> set = new TreeSet<Integer>();
set.add(2);
set.add(22);
set.add(24);
set.add(2);
for (int i : set) {
System.out.println(i);
}
}

输出结果

2
22
24

  可以知道,TreeSet集合不仅可以保证集合元素的唯一性,还可以排序。

  如果TreeSet里面存储的是对象呢?会出现什么情况呢?

public static void main(String[] args) {
User u1 = new User("Kevin", 72);
User u2 = new User("Jack", 22);
User u3 = new User("Erik", 32);
User u4 = new User("Blank", 72);
Set<User> set = new TreeSet<User>();
set.add(u1);
set.add(u2);
set.add(u3);
set.add(u4);
for (User u :
set) {
System.out.println(u);
}
}

输出结果

Exception in thread "main" java.lang.ClassCastException: com.kevin.exercise10.User cannot be cast to java.lang.Comparable
at java.util.TreeMap.put(TreeMap.java:542)
at java.util.TreeSet.add(TreeSet.java:238)
at com.kevin.exercise10.TreeSetTest.main(TreeSetTest.java:23)

  报错了,因为集合里面的是对象,对象不能转换为比较可比较对象。

  如果想根据年龄排序,打印出各个对象(toString方法),应该怎么做呢?在API里面搜索一下Comparable,发现是个接口,那么我们就可以让Students类实现Comparable接口方法,这样Students对象就成为了可比较对象了。

  User类实现Comparable接口方法:

public class User implements Comparable<User> {
private String name;
private int age; @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public int compareTo(User u) {
int num = this.age - u.age;
return this.age - u.age;
} public User(String name, int age) {
this.name = name;
this.age = age;
} public User() { }
}

  为什么是this.age-u.age?this.age代表调用时的对象的age,返回的如果是正数(比u.age大),就存储在右边。返回的是如果是负数(比u.age小),就存储在左边。如果等于0,就不存储。

  这就出问题了,如果两个人不同名字,同样年龄,this.age - o.age = 0,不就存不进二叉树了吗?

验证一下:

public static void main(String[] args) {
User u1 = new User("Kevin", 72);
User u2 = new User("Jack", 22);
User u3 = new User("Erik", 32);
User u4 = new User("Blank", 72);
Set<User> set = new TreeSet<User>();
set.add(u1);
set.add(u2);
set.add(u3);
set.add(u4);
for (User u :
set) {
System.out.println(u);
}
}

输出结果:

User{name='Jack', age=22}
User{name='Erik', age=32}
User{name='Kevin', age=72}

  所以需要改进一下User类的compareTo方法,保证同年龄,但是不同名字的学生也能存进二叉树。

@Override
public int compareTo(User u) {
int num = this.age - u.age;
return num == 0 ? this.name.compareTo(u.name) : num;
}

  这样就可以把四个不同的对象存储进来,并且先按照年龄排序,年龄相同的再按照字符串排序。

输出结果:

User{name='Jack', age=22}
User{name='Erik', age=32}
User{name='Blank', age=72}
User{name='Kevin', age=72}

---------------------------------------------------------------------------------------

除了这种方式可以实现排序以外,还有一种方式可以实现排序。

  TreeSet有这么一个构造方法:

  TreeSet(Comparator<? super E> comparator) 构造一个新的,空的树集,根据指定的比较器进行排序。

  Comparator是什么呢?API文档看一下:Interface Comparator<T>,是一个接口,里面有一个要实现的接口方法:int compare(T o1, T o2) 比较其两个参数的顺序。

  例如,我们要对字符串的长度进行排序,长度相同的安装字符串排序:

public class TreeSetTest2 {
public static void main(String[] args) {
TreeSet<String> set = new TreeSet<String>(new SortedByLen());
set.add("abc");
set.add("abcd");
set.add("b");
set.add("a");
set.add("abab");
System.out.println(set);
}
} class SortedByLen implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
int num = s1.length() - s2.length();
return num == 0 ? s1.compareTo(s2) : num;
} }

  需要注意的是重写compare方法的o1,o2。o1代表调用的对象,o2代表集合中的对象。两种实现排序方式视情况而用。

(1)自然顺序(Comparable)

  • TreeSet类的add()方法中会把存入的对象提升为Comparable类型
  • 调用对象的compareTo()方法和集合中的对象比较
  • 根据compareTo()方法返回的结果进行存储

(2)比较器顺序(Comparator)

  • 创建TreeSet的时候可以指定一个Comparator
  • 如果传入了Comparator的子类对象,那么TreeSet就会按照比较器中的顺序排序
  • 调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数

(3)两种方式的区别

  • TreeSet构造函数什么都不传,默认按照类中Comparable的顺序(没有就报错ClassCastException)
  • TreeSet如果传入Comparator,就优先按照Comparator

  如果不想保证元素的唯一性,改一下compare方法就可以了,永远不要让它返回0。

原文参考【https://www.javazhiyin.com/

TreeSet集合解析的更多相关文章

  1. TreeSet集合的add()方法源码解析(01.Integer自然排序)

    >TreeSet集合使用实例 >TreeSet集合的红黑树 存储与取出(图) >TreeSet的add()方法源码     TreeSet集合使用实例 package cn.itca ...

  2. map,set,list等集合解析以及HashMap,LinkedHashMap,TreeMap等该选谁的的区别

    前言: 今天在整理一些资料时,想起了map,set,list等集合,于是就做些笔记,提供给大家学习参考以及自己日后回顾. Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允 ...

  3. TreeSet集合深入了解--------攻击原理

    Set接口Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false.(无序,不可重复 )Set判断两个对象相同不是使用==运算符,而是根据equals方法.也就是说 ...

  4. TreeSet集合

    TreeSet集合 TreeSet集合是一个依靠TreeMap实现的有序集合,内部存储元素是自动按照自然排序进行排列,所以如果想要保留存储时的顺序,那么就不建议使用TreeSet. TreeSet继承 ...

  5. Java TreeSet集合排序 && 定义一个类实现Comparator接口,覆盖compare方法 && 按照字符串长度排序

    package TreeSetTest; import java.util.Iterator; import java.util.TreeSet; import javax.management.Ru ...

  6. TreeSet集合如何保证元素唯一

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

  7. TreeSet集合排序方式一:自然排序Comparable

    TreeSet集合默认会进行排序.因此必须有排序,如果没有就会报类型转换异常. 自然排序 Person class->实现Comparable,实现compareTo()方法 package H ...

  8. TreeSet集合的自然排序与比较器排序、Comparable接口的compareTo()方法

    [自然排序] package com.hxl; public class Student implements Comparable<Student> { private String n ...

  9. java-Set集合、HashSet集合、LinkedHashSet集合和TreeSet集合

    1.Set集合,无索引,不可以重复,无序(存取不一致) public class Demo { public static void main(String[] args) { //demo1(); ...

随机推荐

  1. 115个Java面试题和答案——终极列表(下)【转】

    第一篇讨论了面向对象编程和它的特点,关于Java和它的功能的常见问题,Java的集合类,垃圾收集器,本章主要讨论异常处理,Java小应用程序,Swing,JDBC,远程方法调用(RMI),Servle ...

  2. python 整理数据中的图片

    import shutil import time import pymysql conn=pymysql.connect(host="222",user="root&q ...

  3. PAT1002:A+B for Polynomials

    1002. A+B for Polynomials (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue T ...

  4. linux几种时间函数总结

    一.linux时间函数总结 最近的工作中用到的时间函数比较频繁,今天抽时间总结一下,在linux下,常用的获取时间的函数有如下几个:  asctime,  ctime, gmtime, localti ...

  5. Enabling Chrome Developer Tools inside Postman

    Chrome's Developer Tools are an indispensable part of the modern web development workflow. However, ...

  6. dva/docs/GettingStarted.md

    进入目录安装依赖: npm i 或者 yarn install开发: npm run dev npm install 太慢,试试yarn吧.建议用npm install yarn -g进行安装. Co ...

  7. 你不知道的JavaScript--Item16 for 循环和for...in 循环的那点事儿

    大家都知道在JavaScript中提供了两种方式迭代对象: for 循环: for..in循环: 1.for循环 不足: 在于每次循环的时候数组的长度都要去获取: 终止条件要明确: 在for循环中,你 ...

  8. 区分IE8 、IE9 、IE10的专属css hack

    想让IE8及以下的浏览器实现同样的效果,且不希望使用css3pie或htc或条件注释等方法时,可能就会需要用到IE8和IE9的专属css hack了. .test{ /* 1. */ color:#0 ...

  9. python3[爬虫实战] 使用selenium,xpath爬取京东手机

    使用selenium ,可能感觉用的并不是很深刻吧,可能是用scrapy用多了的缘故吧.不过selenium确实强大,很多反爬虫的都可以用selenium来解决掉吧. 思路: 入口: 关键字搜索入口 ...

  10. if_else_while_for

    import getpass #标准库里要加密密码需要导包getpass.但是getpass在pycharm中不好用,需要在命令窗口中输入才管用. _username = "abc" ...