原创

Java 中 TreeSet 集合类排序方法

        TreeSet是依靠TreeMap来实现的。TreeSet是一个有序集合,TreeSet中的元素将按照升序排列,缺省是按照自然排序进行排列,意味着TreeSet中的元素要实现Comparable接口。或者有一个自定义的比较器。TreeSet支持两种排序方法:自然排序和定制排序,TreeSet默认采用自然排序。


Java常用类实现Comparable接口,并提供了比较大小的标准。实现Comparable接口的常用类:

BigDecimal、BigIneger以及所有数值型对应包装类:按它们对应的数值的大小进行比较;
Character:按字符的UNICODE值进行比较;
Boolean:true对应的包装类实例大于false对应的包装类实例;
String:按字符串中字符的UNICODE值进行比较;
Date、Time:后面的时间、日期比前面的时间、日期大。


1、添加一个对象到TreeSet时,对象类必须实现Comparable接口,代码如下:

package com.yoodb;

import java.util.TreeSet;

public class Error{
	
	public static void main(String[] args) {
		TreeSet<Object> set = new TreeSet<Object>();
		set.add(new Error());
		set.add(new Error());
	}
}

其结果报错,错误信息如下:

Exception in thread "main" java.lang.ClassCastException: com.Error cannot be cast to java.lang.Comparable
	at java.util.TreeMap.compare(TreeMap.java:1188)
	at java.util.TreeMap.put(TreeMap.java:531)
	at java.util.TreeSet.add(TreeSet.java:255)
	at com.Error.main(Error.java:9)


注意:

1)如果向TreeSet集合中添加2个Error对象,添加第一个对象时,TreeSet里没有任何元素因此没有问题,但添加第二个Error对象时TreeSet就会调用该对象的compareTo(Object obj)方法与集合中其他元素进行比较,如果对应的类没有实现Comparable接口,则会引发ClassCastException异常;从TreeSet中取出元素第一个元素时,依然会引发ClassCastException异常。

2)采用compareTo(Object obj)方法比较对象时,都需要将被比较对象obj强制类型转换成相同类型,因为只有相同类的两个实例才能比较大小。即向TreeSet中添加的应该是同一个类的对象,否则会引发ClassCastException异常。

3)当把一个对象加入TreeSet集合中时,TreeSet调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树算法决定它的存储位置,如果两个对象通过compareTo(Object obj)比较相等,TreeSet即认为它们存储同一位置。(如果对红黑树算法不是很了解可以去网上查询一下)

4)TreeSet集合判断两个对象不相等的标准:两个对象通过equals方法比较返回false,或通过compareTo(Object obj)比较没有返回0——即使两个对象时同一个对象,TreeSet也会把它们当成两个对象进行处理,具体代码如下:

package com.yoodb;

import java.util.TreeSet;

class A implements Comparable {
	int age;

	public A(int age) {
		this.age = age;
	}

	public boolean equals(Object obj) {
		return false;
	}

	public int compareTo(Object obj) {
		return 1;
	}
}

public class TestTreeSet {
	public static void main(String[] args) {
		TreeSet<Object> set = new TreeSet<Object>();
		A a1 = new A(21);
		set.add(a1);
		System.out.println(set.add(a1));
		System.out.println(set);
		((A) (set.first())).age = 25;
		System.out.println(((A) (set.last())).age);
	}
}


结果运行如下:

true
[com.A@73cbc5cb, com.A@73cbc5cb]
25


1)把同一对象添加两次,a1对象的equals方法返回false,而且compareTo(Object obj)方法总是返回1,TreeSet会认为a1对象和它自己也不相同,因此TreeSet中添加两个a1对象。而TreeSet对象保存的两个元素实际上是同一个元素。所以当修改TreeSet集合里第一个元素的age属性后,该TreeSet集合里最后一个元素的age属性也随之改变了。

2)两个对象通过equals方法比较返回true,通过compareTo(Object obj)方法比较不返回0时TreeSet将会把这两个对象保存在不同位置,从而两个对象都可以添加成功;

3)两个对象通过equals方法比较返回false,通过compareTo(Object obj)方法比较返回0两个对象通过compareTo(Object obj)方法比较相等,TreeSet将试图把它们保存在同一个位置,但实际上又不行(否则将只剩下一个对象);


2、定制排序

实现定制排序需要在创建TreeSet集合对象时提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。具体实现代码如下:

package com.yoodb;

import java.util.Comparator;
import java.util.TreeSet;

class Age {
	int age;

	public Age(int age) {
		this.age = age;
	}

	public String toString() {
		return "age 对象 (age:" + age + ")";
	}
}

public class TestTreeSet {
	public static void main(String[] args) {
		TreeSet<Object> ts = new TreeSet<Object>(new Comparator<Object>() {
			public int compare(Object o1, Object o2) {
				Age a1 = (Age) o1;
				Age m2 = (Age) o2;
				if (a1.age > m2.age) {
					return -1;
				} else if (a1.age == m2.age) {
					return 0;
				} else {
					return 1;
				}
			}
		});
		ts.add(new Age(21));
		ts.add(new Age(25));
		ts.add(new Age(-9));
		System.out.println(ts);
	}
}


运行程序结果如下:

[M 对象 (age:25), M 对象 (age:21), M 对象 (age:-9)]


创建一个Comparator接口的匿名内部类对象,负责ts集合的排序。当把Age对象添加到集合中时,无须Age类实现Comparable接口,此时TreeSet无须通过Age对象来比较大小,而是由与TreeSet关联的Comparator对象来负责集合元素的排序。使用定制排序时,TreeSet对集合元素排序时不管集合元素本身的大小,而是由Comparator对象负责集合元素的排序规则。

关注下方微信公众号“Java精选”(w_z90110),回复关键字领取资料:如HadoopDubboCAS源码等等,免费领取资料视频和项目。 

涵盖:程序人生、搞笑视频、算法与数据结构、黑客技术与网络安全、前端开发、Java、Python、Redis缓存、Spring源码、各大主流框架、Web开发、大数据技术、Storm、Hadoop、MapReduce、Spark、elasticsearch、单点登录统一认证、分布式框架、集群、安卓开发、iOS开发、C/C++、.NET、Linux、Mysql、Oracle、NoSQL非关系型数据库、运维等。

评论

分享:

支付宝

微信