Set集合TreeSet学习(Set集合学习二)
4、TreeSet: 可以对Set集合中的元素进行排序。是不同步的
4.1、TreeSet保证元素唯一的方式
TreeSet使用元素的自然顺序(字典顺序)对元素进行排序【TreeSet集合排序方式一:让元素自身具备比较功能】
或者
根据创建 set 时提供的 Comparator (比较器)进行排序,具体取决于使用的构造方法。【TreeSet集合排序方式二:让集合自身具备比较功能】
判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。
(1)TreeSet集合排序方式一:让元素自身具备比较功能
让元素自身具备比较功能,只就需要实现Comparable接口。覆盖compareTo方法。【Comparable接口的compareTo方法】
即:自定义对象实现Comparable接口,定义排序方式,实现comparaTo方法。
(2)特殊场景分析
场景:
如果不要按照对象中具备的自然顺序进行排序,怎么办?
对象不是自己的定义的,而是使用别人定义好的,我们不能对这个对象做任何修改,而且这个对象没有实现Comparable接口,怎么办?
这个对象不具有比较性!或者说,这个对象具备比较性,实现了Comparable接口,但是这个对象具备的比较性,不是我们需要的,我们该怎么办?
解决方案:
使用TreeSet集合排序方式二:让集合自身具备比较功能
(3)TreeSet集合排序方式二:让集合自身具备比较功能
让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。将该类对象作为参数传递给TreeSet集合的构造函数。
4.2、TreeSet集合使用注意
(1)TreeSet集合排序方式一:让元素自身具备比较功能
【1】当元素为自定义对象(eg:Person类)时,让元素自身具备比较功能,实现Comparable接口,覆盖compareTo方法
【2】当元素为字符串对象(eg:“abd”)或者jdk中的对象有实现Comparable接口的方法时,由于这些对象本身已经实现了Comparable接口的compareTo方法,本身就具备比较功能,直接使用TreeSet即可。
(2)TreeSet集合排序方式二:让集合自身具备比较功能
让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。将该类对象作为参数传递给TreeSet集合的构造函数。
public TreeSet(Comparator<? super E>comparator)构造时传递参数
构造一个新的空 TreeSet,它根据指定比较器进行排序。插入到该 set 的所有元素都必须能够由指定比较器进行相互比较:对于 set 中的任意两个元素 e1 和 e2,执行 comparator.compare(e1, e2) 都不得抛出 ClassCastException
(3)排序优先
当TreeSet集合使用时,如果元素本身具备比较功能;
然后又定义一个类实现Comparator接口,覆盖compare方法,并该类对象作为参数传递给TreeSet集合的构造函数。使得TreeSet集合自身具备比较功能。
这时候,TreeSet的排序方法以集合自身具备的比较功能为主(即:TreeSet集合排序方式二为主,忽略TreeSet集合排序方式一)
结论:当TreeSet集合有两种比较功能时,TreeSet集合排序方式二(集合自身具备比较功能)优先于 TreeSet集合排序方式一(元素本身具备比较功能)
4.3、TreeSet集合排序案例
(1)TreeSet集合排序方式一案例:
让元素自身具备比较功能
代码实现:
【1】集合排序1测试类
package list_set;
import java.util.Iterator;
import java.util.TreeSet;
/*
* TreeSet集合排序方式一:
* 让元素自身具备比较功能,就需要实现Comparable接口。覆盖compareTo方法。
*/
public class TreeSetFirst {
public static void main(String[] args) {
TreeSet set=new TreeSet();
set.add(new Person("张三",23));
set.add(new Person("张三",23));//去除重复 让元素自身具备比较功能
set.add(new Person("李四",24));//去除重复
set.add(new Person("李四",24));
set.add(new Person("王五",24));
set.add(new Person("赵六",25));
set.add(new Person("你好",26));
//遍历
for(Iterator it=set.iterator();it.hasNext();){
Person p=(Person)it.next();
System.out.println(p.getName()+":"+p.getAge());
}
}
}
【2】Person自定义对象
/*
* 让person元素本身具备比较功能
* 实现Comparable接口。覆盖compareTo方法。
*/
class Person implements Comparable{
private String name;
private int age;
public Person(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 compareTo(Object o) {//定义比较方式
/*
*1.年纪小的排在前面
*2.当年纪相同是,名字的自然排序小于0的排在前面
*/
Person p=(Person)o;
int ageCompare=this.getAge()-p.getAge();
if(ageCompare==0){
//小于0 在前;等于0 相等;大于0 在后
return this.getName().compareTo(p.getName());//自然排序
// return this.name.hashCode()-p.name.hashCode();//hashcode值
}
return ageCompare;//小于0 在前;大于0 在后
}
}
结果:
张三:23
李四:24
王五:24
赵六:25
你好:26
(2)TreeSet集合排序方式二案例:
让集合自身具备比较功能
代码实现:
【1】自定义比较器
/*
* 自定义比较器实现Comparator接口,覆盖compare方法。
*/
class MyComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
/*
*1.年纪小的排在前面 2.当年纪相同是,名字的自然排序小于0的排在前面
*/
Person p1 = (Person) o1;
Person p2 = (Person) o2;
int ageCompare = p1.getAge() - p2.getAge();
if (ageCompare == 0) {
return p1.getName().compareTo(p2.getName());//自然排序
// return p1.getName().hashCode() -p2.getName().hashCode();//hashcode值
// 小于0 在前;等于0相等;大于0在后
} return ageCompare;// 小于0 在前;大于0 在后
}
}
【2】集合排序2测试类
package list_set;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/*
* TreeSet集合排序方式二:
* 让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。
* 将该类对象作为参数传递给TreeSet集合的构造函数。
*/
public class TreeSetSecond {
public static void main(String[] args) {
TreeSet set = new TreeSet(new MyComparator());//调用自定义比较器,让集合自身具备比较功能
set.add(new Person("张三", 23));
set.add(new Person("张三", 23));// 去除重复让集合自身具备比较功能
set.add(new Person("李四", 24));// 去除重复
set.add(new Person("李四", 24));
set.add(new Person("王五", 24));
set.add(new Person("赵六", 25));
set.add(new Person("你好", 26));
// 遍历
for (Iterator it = set.iterator(); it.hasNext();) {
Person p = (Person) it.next();
System.out.println(p.getName() + ":" + p.getAge());
}
}
}
结果:
张三:23
李四:24
王五:24
赵六:25
你好:26
4.4、TreeSet集合排序规则的分析
参考博客园
博文:Java深入了解TreeSet
地址:http://www.cnblogs.com/yzssoft/p/7127894.html
(1)代码
package list_set;
/*
* 让person元素本身具备比较功能
* 实现Comparable接口。覆盖compareTo方法。
*/
class Persons implements Comparable {
private String name;
private int age;
public Persons(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 compareTo(Object o) {// 定义比较方式
return 0; // 当compareTo方法返回0的时候集合中只有一个元素
// return 1; // 当compareTo方法返回正数的时候集合会怎么存就怎么取
// return -1; // 当compareTo方法返回负数的时候集合会倒序存储
}
}
(2)代码现象分析
为什么返回0,只会存一个元素,返回-1会倒序存储,返回1会怎么存就怎么取呢?
原因:在于TreeSet底层其实是一个二叉树机构,且每插入一个新元素(第一个除外)都会调用compareTo()的方法去和上一个插入的元素作比较,并按二叉树的结构进行排列。
1. 如果将compareTo()方法的返回值固定为0,元素值每次比较,都认为是相同的元素,这时就不再向TreeSet中插入除第一个外的新元素。所以TreeSet中就只存在插入的第一个元素。
2. 如果将compareTo()方法的返回值固定为1,元素值每次比较,都认为新插入的元素比上一个元素大,于是二叉树存储时,会存在根的右侧,读取时就是正序排列的。
3. 如果将compareTo()方法的返回值固定为-1,元素值每次比较,都认为新插入的元素比上一个元素小,于是二叉树存储时,会存在根的左侧,读取时就是倒序排列的。
(3)结论
同理,Comparator(比较器)的compare方法返回结果和Comparable接口的compareTo方法返回结果的正负情况,决定了TreeSet集合的排序情况
返回结果为正数时,当前元素,比上一个元素大
返回结果为负数时,当前元素,比上一个元素小
返回结果为0时,当前元素和上一个元素相同。
(4)三种情况案例
1情况一:CompareTo方法中,直接返回return 0(集合只有一个元素)
1.1compareTo方法
public int compareTo(Object o) {// 定义比较方式
return 0; // 当compareTo方法返回0的时候集合中只有一个元素
}
1.2测试代码
package list_set;
import java.util.Iterator;
import java.util.TreeSet;
public class Test1 {
public static void main(String[] args) {
TreeSet set=new TreeSet();
set.add(new Persons("张三",23));
set.add(new Persons("李四",24));
set.add(new Persons("王五",24));
set.add(new Persons("赵六",25));
set.add(new Persons("你好",26));
//遍历
for(Iterator it=set.iterator();it.hasNext();){
Persons p=(Persons)it.next();
System.out.println(p.getName()+":"+p.getAge());
}
}
}
1.3结果【只有一个元素】
张三:23
2 情况二:CompareTo方法中,直接返回return 1(集合正序排列[实现怎么存进来,就怎么取出])
2.1compareTo方法
public int compareTo(Object o) {// 定义比较方式
return 1; // 当compareTo方法返回正数的时候集合会怎么存就怎么取
}
2.2测试代码
同:三种情况案例的情况一中的4.1.2测试代码
2.3结果【正序输出】
张三:23
李四:24
王五:24
赵六:25
你好:26
3 情况三:CompareTo方法中,直接返回return -1(集合逆序排列)
3.1compareTo方法
public intcompareTo(Object o) {// 定义比较方式
return -1; // 当compareTo方法返回负数的时候集合会倒序存储
}
3.2测试代码
同:三种情况案例的情况一中的4.1.2测试代码
3.3结果【逆序输出】
你好:26
赵六:25
王五:24
李四:24
张三:23
4.5、应用:对字符串进行长度排序
(1)字符串长度比较器
package list_set;
import java.util.Comparator;
/*
* 字符串长度比较器
*/
public class ComparatorByLength implements Comparator {
@Override
public int compare(Object o1, Object o2) {
//1.先比较字符串长度,如果长度不同,则比较结束,否则,进入2
//2.比较字符串的字典顺序
String s1=(String)o1;
String s2=(String)o2;
int temp=s1.length()-s2.length();
return temp==0?s1.compareTo(s2):temp;
}
}
(2)字符串长度排序测试
package list_set;
import java.util.Iterator;
import java.util.TreeSet;
/*
* 对字符串进行长度排序
*/
public class CompareByLengthTest {
public static void main(String[] args) {
TreeSetts = new TreeSet(new ComparatorByLength());//字符串长度排序比较器
ts.add("aaaaa");
ts.add("zz");
ts.add("nbaq");
ts.add("cba");
ts.add("abc");
Iterator it = ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
(3)结果
zz
abc
cba
nbaq
aaaaa
4.6思考:TreeSet怎么存进去?怎么取出来?
存:按照TreeSet的两种排序方式的其中一种进行排序,详情看:TreeSet集合排序规则的分析
存的方式,从二叉树的根节点出发,进行比对,如果小于根节点的值,则放在左子树这边;如果大于根节点的值,则放在右子树这边;否则值与根节点相同,则不进行存储
取:从二叉树的最左端的开始取,然后按照【左子树,父节点,右子树】顺序进行取元素。依次类推,进行取,直到结束
说明:左子树,父节点,右子树三者的关系 左子树 < 父节点 <右子树