JAVA集合之——TreeSet

JAVA集合之——TreeSet

    一、TreeSet的排序原理

     TreeSet是有序的,且不可以重复的集合,首先,进行如下测试:

public static void main(String[] args)
{
	//1. 创建一个存储Integer类型的TreeSet
	TreeSet<Integer> set = new TreeSet<Integer>();
	
	//无序添加对像
	set.add(40);
	set.add(10);	
	set.add(10);
	set.add(30);
	set.add(20);
	
	//打印
	//从结果来看数据是有序输出的,并且重复的对像不会被添加
	//TreeSet是一个Sorted Collection
	for(int x: set)
	{
		System.out.println(x);
	}
	
	//2. 创建一个存储String类型的TreeSet
	TreeSet<String> set1 = new TreeSet<>();
	
	//无序添加对像
	set1.add("Hello");
	set1.add("World");
	set1.add("World");
	set1.add("JAVA SE");
	set1.add("JAVA EE");
	
	//打印
	//打印结果是按字符串排序的,并且得复的元素不会被添加
	for(String s: set1)
	{
		System.out.println(s);
	}		
}
    经过测试发现,TreeSet对插入的元素进行了排序,并且不可插入重复的元素,那TreeSet是怎样做到的呢?
    对一个自定义的对像Persion进行如添加元素,打印操作,如下:

public static void main(String[] args)
{
	//创建一个TreeSet
	TreeSet<Persion> set = new TreeSet<>();
	
	//添加自定义元素
	set.add(new Persion("张三", 50));
	set.add(new Persion("李四", 30));
	set.add(new Persion("麻子", 20));
	set.add(new Persion("麻子", 40));
	
	//打印测试
	for(Persion p: set)
	{
		System.out.println(p.getName() + p.getAge());
	}
}
    然而,当运行的时候,出错了,错误信息如下:
    TreeSet.Persion cannot be cast to java.lang.Comparable
    原来,在默认的情况下,TreeSet假定插入的元素产现了Comparable接口,关于Comparable接口,JDK文档抄录如下:

    接口 Comparable<T>
    类型参数:T - 可以与此对象进行比较的那些对象的类型
    接口原型:

public interface Comparable<T>
{
	int compareTo(T other);
}
    此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的 自然排序,类的compareTo 方法被称为它的 自然比较方法。实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。 实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。对于上面的Iteger类型按大小排序,对于String类,它的commpareTo方法依据字典序对字符串进行比较。这些类是JAVA标准类库,已经实现了Comparable接口。
    如果要插入自定义对像,就必须实现Comparable接口自定义排序规则,即实现compareTo方法,这个方法的算法如何实现,就看自已的需求了,如果按照年年龄进行排序,那么Persion类的定义如下:

public class Persion implements Comparable<Persion>
{
	private String name;
	private int	   age;
	
	public Persion(String name,int age)
	{
		this.name = name;
		this.age  = age;
	}
	
	public String getName()
	{
		return this.name;
	}
	
	public int getAge()
	{
		return this.age;
	}
	
	//自定义类,要实现Comparable接口的compareTo方法
	public int compareTo(Persion other)
	{
		return this.age - other.age;
	}	
}
    如上代码所示,compareTo方法的实现是依照Persion对像的年龄比较,所以重新运行主程序代码,添加元素后,打印输出是按年龄排序的。
    实际上,在使用TreeSet存储对像时,它的add()方法是会自动的调用compareTo方法来进行对像的比较,如果相同的元素,就不存储,不相同则按照红黑树(一种自平衡的二叉树)的算法进行存储。
    即,如果a和b相等,调用a.compareTo(b)一定返回0;如果排序后a位于b之前,则返回负值;如果a位于b之后,则返回正值。

    对于上面的例子,如果添加一行:
    set.add(new Persion("小明", 50));
    经测试发现这个元素是没有被添加进去的。为什么?
    上面的compareTo方法是按age进行比较的,age相同,则被认为是相同的对像,所以没有添加进去,所以对于compareTo方法的实现方式,是要根据需求来定的,经过更改,我们采用下面的方式,看起来更完善一些。
public int compareTo(Persion other)
{
	//首先按age排序
	int num = this.age - other.age;
	
	//如果age相同按name排序
	if(num == 0)
	{
		return this.name.compareTo(other.name);
	}
	
	//执行到此表示age不同,返回比较结果
	return num;
}
    经测试,name和age都相同的元素添加不进去,有一个不同的元素可以添加,验证了我们的想法。 


    二、Comparable与Comparator的区别。
    使现Comparable接口的compareTo方法有一定的局限性。对于一个给定的类,只能够实现这个接口一次。对于上面的Persion类,在一个集合中需要按age进行排序, 在另一个集中需要按name进行排序,那要怎么办? 另外,如果Persion类的实现者没有实现Comparable接口中,又该怎么办呢?
这时,Comparator就表现出了极大的灵活性了,TreeSet有一个构造方法:
    TreeSet(Comparator<? super E> comparator) 
         --------- 构造一个新的空 TreeSet,它根据指定比较器进行排序。
    如上面所示,在创建集合时,将自已实现的Comparator对像传递给TreeSet构造器,Comparator也是一个接口,它的完整描述如下:

public interface Comparator<T>
{
	int compare(T a,T b);
}
    与compareTo方法一样,如果a位于b之前,compare方法返回负值;如果a和b相等,则返回0;否则返回正值。查JDK文档发现,该接口还有一个equals方法,但是不需要实现。
    通常,使用匿名内部类的方式将实现的Comparator对像传递给TreeSet构造器:
TreeSet<Persion> set = new TreeSet<>(new Comparator<Persion>()
        		{
        			public int compare(Persion a, Persion b)
        			{
            			//首先按age排序
            			int num = a.getAge() - b.getAge();
            			
            			//如果age相同按name排序
            			if(num == 0)
            			{
            				return a.getName().compareTo(b.getName());
            			}
            			
            			//执行到此表示age不同,返回比较结果
            			return num;
        			}
        		});
    这样的话,对于基本的Persion是固定的,而在不同的集合中却可以使用不同的排序规则对集合元素进行排序,灵活性大大提高。

 

    总结,TreeSet集合保证元素有序和唯一的原理:

    1. 唯一性: 调用add()时通过比较返回是否为0来确确定元素是否相同。

    2. 有序:

        A: 自然排序(元素具备比较性) —— 元素所属的类实现自然排序的Comparable接口。

        B: 比较器排序(集合具备比较性) —— 集合的构造方法接收一个比较器Comparator的子类对像。


    关于HashSet和TreeSet的选择:

    如果不需要对元素进行排序,就没必要付出使用TreeSet所带来的排序开销。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值