1.TreeSet集合概述
TreeSet集合是存储元素不重复,唯一性。TreeSet集合底层数据结构是红黑树(是一种自平衡的二叉树),排序效率高
1.元素不重复的保证:①自然排序;②比较器排序。
2.元素的唯一性:根据比较的返回值是否为0。
2.TreeSet集合两种排序方式
1.自然排序
使用的是无参构造,默认是自然排序。元素所在的类要实现comparable接口,并且要重写compareTo()方法。
2.比较器排序
有参构造,集合的构造方法接收comparator的实现类对象,实现类需要重写compare()方法。
3.TreeSet集合排序方法(多种方法实现,供大家选择)
3.1包装类型的排序方法:
3.1.1.自然排序(依赖compareTo方法,根据返回值是否为0来存储元素。)
升序和降序的方法遍历集合。
package cn.jason01; import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet; // TreeSet集合遍历(自然排序)方法 // 升序和降序 public class TreeSetDemo3 { public static void main(String[] args) { // 采用无参构造,自然排序法. TreeSet<String> ts = new TreeSet<String>(); String s = "linqingxia-林青霞"; String s1 = "zhujielun-周杰伦"; String s2 = "fanxiaoxuan-范晓萱"; String s3 = "zhujielun-周杰伦"; String s4 = "liushishi-刘诗诗"; // 添加元素 ts.add(s); ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); // 升序 ascendIterator(ts); // 降序 descendIterator(ts); // 升序 foreachIterator(ts); } public static void ascendIterator(TreeSet<String> ts) { System.out.println("Ascend-----------\n"); // 集合器遍历 for (Iterator<String> t = ts.iterator(); t.hasNext();) { String st = t.next(); System.out.println(st); } } public static void descendIterator(TreeSet<String> ts) { System.out.println("\nDescend-----------\n"); // 使用TreeSet中的降序方法public Iterator<E> descendingIterator() for (Iterator<String> i = ts.descendingIterator(); i.hasNext();) { String ss = i.next(); System.out.println(ss); } } public static void foreachIterator(TreeSet<String> ts) { System.out.println("\nForeach-----------\n"); // for-each遍历 for (String ss : ts) { System.out.println(ss); } } }
输出结果
Ascend-----------
fanxiaoxuan-范晓萱
linqingxia-林青霞
liushishi-刘诗诗
zhujielun-周杰伦
Descend-----------
zhujielun-周杰伦
liushishi-刘诗诗
linqingxia-林青霞
fanxiaoxuan-范晓萱
Foreach-----------
fanxiaoxuan-范晓萱
linqingxia-林青霞
liushishi-刘诗诗
zhujielun-周杰伦
看一下TreeSet的红黑树存储原理就更好理解了。
3.1.2.比较器排序(还是上面的代码),不同之处是在集合的构造器里面使用了匿名内部类。
匿名内部类前提:必须有一个接口或者一个类。
匿名内部类本质:继承了该类或者实现该接口的子类对象。(就是一个对象)
package cn.jason01;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
// TreeSet集合遍历(自然排序)方法
// 升序和降序
public class TreeSetDemo3 {
public static void main(String[] args) {
// 采用无参构造,自然排序法.
TreeSet<String> ts = new TreeSet<String>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
String s = "linqingxia-林青霞";
String s1 = "zhujielun-周杰伦";
String s2 = "fanxiaoxuan-范晓萱";
String s3 = "zhujielun-周杰伦";
String s4 = "liushishi-刘诗诗";
// 添加元素
ts.add(s);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
// 升序
ascendIterator(ts);
// 降序
descendIterator(ts);
// 升序
foreachIterator(ts);
}
public static void ascendIterator(TreeSet<String> ts) {
System.out.println("Ascend-----------\n");
// 集合器遍历
for (Iterator<String> t = ts.iterator(); t.hasNext();) {
String st = t.next();
System.out.println(st);
}
}
public static void descendIterator(TreeSet<String> ts) {
System.out.println("\nDescend-----------\n");
// 使用TreeSet中的降序方法public Iterator<E> descendingIterator()
for (Iterator<String> i = ts.descendingIterator(); i.hasNext();) {
String ss = i.next();
System.out.println(ss);
}
}
public static void foreachIterator(TreeSet<String> ts) {
System.out.println("\nForeach-----------\n");
// for-each遍历
for (String ss : ts) {
System.out.println(ss);
}
}
}
输出结果
Ascend-----------
fanxiaoxuan-范晓萱
linqingxia-林青霞
liushishi-刘诗诗
zhujielun-周杰伦
Descend-----------
zhujielun-周杰伦
liushishi-刘诗诗
linqingxia-林青霞
fanxiaoxuan-范晓萱
Foreach-----------
fanxiaoxuan-范晓萱
linqingxia-林青霞
liushishi-刘诗诗
zhujielun-周杰伦
这里比较一下这两种做法:
1.要实现升序和降序,自然排序的方法需要写两个方法,一个升序方法,一个降序方法。对于使用构造器来说,只需要foreach遍历就行,因为控制升序还是降序是由比较的返回值控制,上面的例子,return o1.compareTo(o2);就是升序排序(对于Integer类型就是从小到大,对于String来说就是按照字典顺序),return o2.compareTo(o1);就是降序排序。
2.针对包装类型两个差异不大,但是针对自定义对象,拿Student类来说,自然排序要实现Comparable接口,而比较器排序虽说也要实现比较器接口,但是对于调用一次来说,比较器排序还是很占优势,直接匿名对象就ok,而且用完不占用内存,效率大大提高。
3.2自定义对象排序
3.2.1 自然排序(按照年龄排序)
Student类
package cn.jason01;
class Student implements Comparable<Student> {
private static final String Name = null;
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.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 hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int compareTo(Student o) {
// 按照年龄排序
if (this.age != o.age) {
return this.age - o.age;
} else
return this.name.compareTo(o.name);
}
}
TreeSet测试类
package cn.jason01;
import java.util.TreeSet;
//TreeSet存储自定义对象,底层依赖的是Comparable接口
// 刚开始会直接报错,因为没有告诉到底以什么排序。
//要做自然排序,那么必须要实现自然排序接口,还要重写comparTo方法
public class TreeSetDemo1 {
public static void main(String[] args) {
TreeSet<Student> ts=new TreeSet<Student>();
Student s=new Student("linqingxia",23);
Student s1=new Student("zhujielun",25);
Student s2=new Student("fanxiaoxuan",25);
Student s3=new Student("zhujielun",25);
ts.add(s);
ts.add(s1);
ts.add(s2);
ts.add(s3);
for(Student ss:ts){
System.out.println(ss.getName()+"---"+ss.getAge());
}
}
}
输出结果是:
linqingxia---23
fanxiaoxuan---25
zhujielun---25
3.2.2.比较器排序(按姓名长度来比较)
按照姓名长度来排序估计也有不少人比较困惑,在这里说一下思路。先用姓名长度比较,然后在比较字典顺序。有的人喜欢用三目运算做,个人觉得三目运算在一两个条件的时候代码还是比较清晰的,但是条件多的时候用三目反倒代码不清晰,个人推荐使用if语句。
Student01类
package cn.jason01;
public class Student01 {
private String name;
private int age;
public Student01() {
super();
}
public Student01(String name, int age) {
super();
this.name = name;
this.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 hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student01 other = (Student01) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
比较器Comparator的外部实现类
package cn.jason01;
import java.util.Comparator;
//按照长度排序
public class MyComparerDemo implements Comparator<Student01> {
//因为这里要是在用this代表的是new MyComparerDemo()对象了,所以传了两个对象o1和o2
@Override
public int compare(Student01 o1, Student01 o2) {
// 先比较字符串的长度,我把长度先排好(一步一步来)
//如果o1长度大于o2那么,根据链表,o1挂在右边
if(o1.getName().length()>o2.getName().length())
return 1;
//如果o1长度小于o2那么,根据链表,o1挂在左边
if(o1.getName().length()<o2.getName().length())
return -1;
//对于长度相等,内容相等的情况,我先不管,后面比较字典顺序就行,先搞定一个。
//如果字典顺序相同,直接用比较年龄
if(o1.getName().compareTo(o2.getName())==0)
return o1.getAge()-o2.getAge();
//如果字典顺序不同,那么直接按照字典顺序就行。
return o1.getName().compareTo(o2.getName());
}
}
TreeSet测试类
package cn.jason01;
import java.util.Comparator;
import java.util.TreeSet;
/*比较方法:
1.TreeSet的无参构造(元素具有可比性)
让元素所属的类实现Comparable接口,然后重写compareTo方法
2.TreeSet的有参(接收比较器)(集合具有可比性)
让集合的构造方法接收一个比较器的子类对象*/
public class TreeSetComparer {
public static void main(String[] args) {
TreeSet<Student01> ts=new TreeSet<Student01>(new MyComparerDemo());
Student01 s=new Student01("linqingxia",27);
Student01 s1=new Student01("zhangguorong",29);
Student01 s2=new Student01("wanglihong",23);
Student01 s3=new Student01("liushishi",22);
Student01 s4=new Student01("wuqilong",40);
Student01 s5=new Student01("fengqingy",22);
Student01 s6=new Student01("linqingxia",29);
ts.add(s);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
for(Student01 ss:ts){
System.out.println(ss.getName()+"---"+ss.getAge());
}
}
wuqilong---40
fengqingy---22
liushishi---22
linqingxia---27
linqingxia---29
wanglihong---23
zhangguorong---29
比较器Comparator的内部实现类
package cn.jasonTreeSet;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
/**
* 这是按照姓名长度来比较
*
* @author Jason
*/
public class SortAsLength {
public static void main(String[] args) {
Set<Student> set = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 先按照长度排序
// 长度不同直接按照长度排序
if (o1.getName().length() != o2.getName().length())
return o1.getName().length() - o2.getName().length();
// 然后按照字典顺序排好
if (o1.getName().compareTo(o2.getName()) != 0)
return o1.getName().compareTo(o2.getName());
// 漏掉的肯定是长度和字典顺序相同的情况,那么对于这种情况直接比较年龄即可
return o1.getAge() - o2.getAge();
}
});
Student01 s = new Student01("linqingxia", 27);
Student01 s1 = new Student01("zhangguorong", 29);
Student01 s2 = new Student01("wanglihong", 23);
Student01 s3 = new Student01("liushishi", 22);
Student01 s4 = new Student01("wuqilong", 40);
Student01 s5 = new Student01("fengqingy", 22);
Student01 s6 = new Student01("linqingxia", 29);
set.add(s);
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
set.add(s6);
// 定义统计变量
for (Student student : set) {
System.out.println(student.getName() + "---" + student.getAge());
}
}
}
输出结果:
wuqilong---40
fengqingy---22
liushishi---22
linqingxia---27
linqingxia---29
wanglihong---23
zhangguorong---29
4.总结