ThreeSet能够对集合中的对象排序,当TreeSet想集合中加入一个对象时,会把它插入到有序的对象序列中。那么TreeSet是如何排序呢?TreeSet支持两种排序方式:自然排序和客户化排序.在默认情况下TreeSet采用自然排序方式。
先来介绍介绍什么是自然排序吧
1、自然排序
在JDK类库中,有一部分类实现了Comparable接口,如Integer Double和String等。
Comparable接口有一个comparTo(Object o)方法,它返回整数类型。对于表达式x.compareTo(y),如果返回值为0,则表示x和y相等,如果返回值大于0,则表示x大于y,如果返回值小于0,则表示x小于y.TreeSet集合调用对象的compareTo()方法比较集合中的大小,注意鸟 不是TreeSet调用它自己的comparTo()方法而是它调用集合中对象的comparTo()方法.TreeSet类本身并没有实现Comparable接口,然后进行升序排列,这种方式称为自然排序.
有人可能要问TreeSet集合怎么给对象排序的按对象的什么排序的?
下面简单总结一哈
JDK类库中实现了Comparable接口的一些类的排序方式
类 BigDecimal BigInteger Byte Double Float Integer Long Short 排序方式是 |
按数字大小排序 |
类 Character是 |
按字符的Unicode值的数字大小排序 |
类 String是 |
按字符中字符的Unicode值排序 |
这里一定要灰常注意:使用自然排序时只能向集合中加入同类型的对象,并且这些对象的类必须实现Comparable接口
下面来说说Comparable接口和Comparator接口的区别
Comparator位于包java.util下,而Comparable位于包 java.lang下
Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口) 此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
比如你有一个Customer类 想让这个类的实例加入集合后自动就具有某种排序功能只要这些实例加入集合后 就会按照你给Customer对象设定的方式排序
代码:
package hang.jihe;
import java.util.HashSet;
import java.util.Set;
public class Customer implements Comparable {
private String name;
private int age;
public Customer(String name, int age) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Customer))
return false;
final Customer other = (Customer) obj;
if (this.name.equals(other.getName()) && this.age == other.getAge())
return true;
else
return false;
}
public static void main(String[] args) {
Set<Customer> set = new HashSet<Customer>();
Customer customer1 = new Customer("Tom", 15);
Customer customer2 = new Customer("Tom", 15);
set.add(customer1);
set.add(customer2);
System.out.println(set.size());
}
public int compareTo(Object o) {
Customer other = (Customer) o;
// 先按照name属性排序
if (this.name.compareTo(other.getName()) > 0)
return 1;
if (this.name.compareTo(other.getName()) < 0)
return -1;
// 在按照age属性排序
if (this.age > other.getAge())
return 1;
if (this.age < other.getAge())
return -1;
return 0;
}
@Override
public int hashCode() {
int result;
result = (name == null ? 0 : name.hashCode());
result = 29 * result + age;
return result;
}
}
main方法的类
package hang.jihe;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class CustomerTester {
public static void main(String[] args) {
Set<Customer> set = new TreeSet<Customer>();
set.add(new Customer("Tom",15));
set.add(new Customer("Tom",20));
set.add(new Customer("Tom",15));
set.add(new Customer("Mike",15));
Iterator<Customer> it = set.iterator();
while(it.hasNext()){
Customer customer = it.next();
System.out.println(customer.getName()+" "+customer.getAge());
}
}
}
//打印结果
Mike 15
Tom 15
Tom 20
有人会问 重写hashCode()方法 equals方法干嘛 别急
慢慢道来
实际上,所有实现 Comparable 的 Java 核心类都具有与 equals 一致的自然排序。java.math.BigDecimal 是个例外,它的自然排序将值相等但精确度不同的 BigDecimal 对象(比如 4.0 和 4.00)视为相等。为了保证TreeSet能正确地排序,要求Customer类的compareTo()方法与equals()方法按相同的规则比较两个Customer对象是否相等.也就是说,如果customer1.equals(customer2)为True,那么customer1.compareTo(customer2)为0。 既然重写了equals方法 就得重写hashCode()方法这个大家都知道
大家看一眼结果便知 这个按照升序排序的 年龄也是按照升序
还有要注意哦,对于TreeSet中已经存在的Customer对象,如果修改了它们的name属性或age属性,则TreeSet不会对集合进行重新排序.例如下边的代码先把customer1和customer2 对象加入到TreeSet集合中,然后修改customer1的age属性
代码:
package hang.jihe;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
Set<Customer> set = new TreeSet<Customer>();
Customer customer1 = new Customer("Tom",15);
Customer customer2 = new Customer("Tom",16);
set.add(customer1);
set.add(customer2);
//customer1.setAge(20);//修改customer1实例的age
Iterator<Customer> it=set.iterator();
while(it.hasNext()){
Customer customer=it.next();
System.out.println(customer.getName()+" "+customer.getAge());
}
}
}
上边我先把
customer1.setAge(20);
这句注释掉
打印结果是:
Tom 15
Tom 16
这个是我们要的结果 name 和age都是按照升序排序的
然后我们把那句取消注释之后的打印结果是:
Tom 20
Tom 16
Tom 20如果按照升序应该在下边 但是却在上边 说明TreeSet没有给它重新排序哦在实际应用中Customer对象的name属性和age属性肯定应该是可以被修改的,因此不适合用TreeSet来排序。那大家也应该能想到最适合用TreeSet排序的就是不可变类了呗 比如Integer,Double,String等 所谓不可变类,是指当创建了这个类的实例后,就不允许修改它的属性值。大家以后用还是小心点好儿!
客户化排序
Comparator这个单词啥意思? 你知道不? 比较器的意思 学好英语还是挺好滴
除了自然排序,TreeSet还支持客户化排序.java.util.Comparator<Type>接口提供具体的排序方式,<Type>指定被比较的对象的类型,Comparator有个compar(Type x,Type y)方法,用于比较两个对象的大小,当compare(x,y)大于0时表示x大于y,小于0表示x小于y
等于0表示x等于y
来个例子如果希望TreeSet按照Customer对象的name属性进行降序排列,可以先创建一个实现Comparator接口的类
代码:
package hang.jihe;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class CustomerComparator implements Comparator<Customer>{
public int compare(Customer c1, Customer c2) {
if(c1.getName().compareTo(c2.getName())>0)return -1;
if(c1.getName().compareTo(c2.getName())<0)return 1;
return 0;
}
public static void main(String args[]){
Set<Customer> set = new TreeSet<Customer>(new CustomerComparator());
Customer customer1= new Customer("Tom",15);
Customer customer3= new Customer("Jack",16);
Customer customer2= new Customer("Mike",26);
set.add(customer1);
set.add(customer2);
set.add(customer3);
Iterator<Customer> it = set.iterator();
while(it.hasNext()){
Customer customer = it.next();
System.out.println(customer.getName()+" "+customer.getAge());
}
}
}
以上main方法在构造TreeSet的实例时,调用了它的TreeSet(Comparator comparator)构造方法.
Set<Customer> set = new TreeSet<Customer>(new CustomerComparator());
这是干甚? 其实就是指定一个比较器 TreeSet集合里边的对象按照这个比较器的规则进行排序 我把TreeSet类的这个构造方法搞上来看看就明白了 TreeSet里边有这样一个构造方法
TreeSet
public TreeSet(Comparator<? super E> comparator)构造一个新的空 TreeSet,它根据指定比较器进行排序。插入到该 set 的所有元素都必须能够由指定比较器进行相互比较:对于 set 中的任意两个元素 e1 和 e2,执行 comparator.compare(e1, e2) 都不得抛出 ClassCastException。如果用户试图将违反此约束的元素添加到 set 中,则 add 调用将抛出 ClassCastException。
参数:
comparator - 将用来对此 set 进行排序的比较器。如果该参数为 null,则使用元素的自然顺序。
最后的打印结果是:
Tom 15
Mike 26
Jack 16
是倒序 ...
那你现在是不知道了comparable接口和comparable接口的区别了并且也能更好的使用TreeSet集合了
总结一下吧
单点解释吧:用自定义类实现Comparable接口,那么这个类就具有排序功能,Comparable和具体你要进行排序的类的实例邦定。而Comparator比较灵活,只需要通过构造方法指定一个比较器就行了实现它的自定义类仅仅定义了一种排序方式或排序规则。不言而喻,这种方式比较灵活。我们的要排序的类可以分别和多个实现Comparator接口的类绑定,从而达到可以按自己的意愿实现按多种方式排序的目的。Comparable——“静态绑定排序”,Comparator——“动态绑定排序”。
在多墨迹一点 说说编写java类时应该养成一些好习惯吧
一: 如果java类重新定义了equals方法,那么这个类也必须重新定义hashCode()方法,并且保证当两个对象用equals方法比较结果为true时,这两个对象的hashCode()方法的返回值相等.
二:如果java类实现了Comparable接口,那么这个类应该从新定义compareTo() equals() 和hashCode()方法,保证compareTo()和equals()方法采用相同的比较规则来比较两个对象是否相等,并且保证当两个对象用equals()方法比较的结果为true时,这两个对象的hashCode()方法的返回值相等.
HashSet和HashMap具有较好的性能,是Set和Map首选实现类,只有在需要排序的场合,才考虑使用TreeSet和TreeMap. LinkedList 和 ArrayList各有优缺点,如果经常对元素执行插入和删除操作,那么可以用LinkedList,如果经常随机访问元素,那么可以用ArrayList.