【Java笔记】TreeSet的自然顺序和制定顺序

TreeSet这个类的特点是:使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的Comparator 进行排序,具体取决于使用的构造方法。

一、按自然顺序排列

我们定义一个employee类,重写equal和hashcoed方法,并重写compareto方法,hsahcode和equal方法是用来让set比较两个元素是否相同,进而决定是否把他添加进集合;而这个类的重写的compareto方法则是决定了这个类的自然顺序,如果没有指定compartor,在add的时候,就会按照这个自然添加进set

在第一个例子中我们用public TreeSet()无参构造方法,先看一下API(jdk1.6)介绍的它的使用特点:

public TreeSet()

构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。插入该 set 的所有元素都必须实现 Comparable 接口。另外,所有这些元素都必须是 可互相比较的:对于 set 中的任意两个元素 e1e2,执行 e1.compareTo(e2) 都不得抛出 ClassCastException。如果用户试图将违反此约束的元素添加到 set(例如,用户试图将字符串元素添加到其元素为整数的 set 中),则 add 调用将抛出 ClassCastException



例1:
public class Employee implements Comparable{
	private int age;
	private String name;
	private int salary;
	protected Employee(){	
	}
	protected Employee(String name,int age,int salary){
		this.age=age;
		this.name=name;
		this.salary=salary;
	}	
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + salary;
		return result;
	}
	@Override
	public boolean equals(Object obj) {   //这是一个自定义的 重写equals方法,认为名字相同的两个对象就是同一个元素,set接口不会接受两个相同的元素
		Employee e1=(Employee) obj;
		if(e1.name==this.name){
			return true;
		}else return false;
	
	}
	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;
	}
	public int getSalary() {
		return salary;
	}
	public void setSalary(int salary) {
		this.salary = salary;
	}
	@Override 
     
    
   
//自定义的compareTo方法,因为TreeSet只能接受实现了compareTo方法的类的对象(必须是可比较的对象),这个compareto会规定了对象的序列,我们称之为类自然顺序;
	public int compareTo(Object j) {
		Employee o=(Employee) j;
		if(name!=null){
			if (!(this.name.equals(o))){
				return this.name.compareTo(o.name);
				}else 
					return Integer.signum(this.age-o.age);

				}
		else return 0;
	
	}
import static org.junit.Assert.*;


import java.util.TreeSet;

import org.junit.Test;

public class TreeSetOfEmployee {
	@Test
	public void testName() throws Exception {
		TreeSet set = new TreeSet();
		 set.add(new Employee("Tom",54,5000) );
		 set.add(new Employee("Tom",28,4000)); //因为我们重写了Employee中的equal和hashcode方法,同名的Tom将不被列入
		 set.add(new Employee("Amy",24,3000));
		 set.add(new Employee("Mark",49,4000));
		
		
		for(Object o: set){
			Employee e1=(Employee) o;
			System.out.println(e1.getName()+"---------"+e1.getAge()+"---------"+e1.getSalary());
		}
	}
} 
     
    
   

输出结果:
Amy---------24---------3000
Mark---------49---------4000
Tom---------54---------5000

可以看出,我们输入的两个Tom,有一个没有进入集合,这是因为我们规定了,名字相同的对象为同一种对象,Set的实现类不允许有相同的元素
再一个,我们可以看出,我们第三个输入的Amy,确实第一个输出的,这其实是因为我们规定了我们的自然序列是以name调的String类里的compareTo方法来比较名字,我们可以看一下API怎么阐述的这个方法的:
public int compareTo(String anotherString)
按字典顺序比较两个字符串。该比较基于字符串中各个字符的 Unicode 值。按字典顺序将此 String 对象表示的字符序列与参数字符串所表示的字符序列进行比较。如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。如果这两个字符串相等,则结果为 0; compareTo 只在方法 equals(Object) 返回 true 时才返回 0
可以看到他是按字典顺序排列,我们观察我们的输出结果,Amy因为首字母是A,跑到了最先面。

二、comparator比较器
comparator是一个接口,我们先看看API对它的解释:
public interface Comparator
 
  

强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sortArrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。

对于我们此处的应用,我们只需要关注最后一句话,为collection提供排序;

我们再来看一下TreeSet的传comparator的一个构造器:

TreeSet

public TreeSet(Comparator comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。插入到该 set 的所有元素都必须能够由指定比较器进行 相互比较:对于 set 中的任意两个元素 e1e2,执行 comparator.compare(e1, e2) 都不得抛出 ClassCastException。如果用户试图将违反此约束的元素添加到 set 中,则 add 调用将抛出 ClassCastException
可以看出,采用这个方法我们需要往里传一个经过实现的comparator对象,我们知道comparator是一个接口,只有通过它的实现类才能构建它的对象,我们需要专门写一个类来实现它,写到这里你想到了什么,没错,这是一个只需要在此处实现一次的类,目的就是为了让我们构建一个对象,我们会想到通过写一个匿名内部类来简化操作;
那我们在这个类里面实现comparator时需要实现它里面的抽象方法,我们来看一下他有那些抽象方法:

int
compare(T o1, T o2)
          比较用来排序的两个参数。

boolean equals(Object obj)
          指示某个其他对象是否“等于”此 Comparator。


我们知道所有的类和接口都是继承自Object这个类的,Object这个类里有equals方法,所以我们实际上只需要实现compare方法就可以了,我们再来看一下API对我们实现时推荐的规范:

compare

int compare(T o1,T o2)
比较用来排序的两个参数。根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。
我们把上面的TreeSetOfEmployee类里的测试代码稍微修改一下
例2:
import static org.junit.Assert.*;

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

import org.junit.Test;

public class TreeSetOfEmployee_comparator {
	@Test	
	public void testName() throws Exception {
		Comparator salarycomparator=new Comparator(){    //这个地方是一个匿名内部类,直接在创建的接口对象后面加花括号写
			@Override
        //重写compare方法,按薪水的来排序
		public int compare(Object o1, Object o2) {
			Employee e1=(Employee) o1;
			Employee e2=(Employee) o2;
			if(e1.getSalary()>e2.getSalary()){
				return 1;
			}else if(e1.getSalary() set = new TreeSet(salarycomparator);
		 set.add(new Employee("Tom",44,5000) );
		 set.add(new Employee("Amy",24,9000));
		 set.add(new Employee("Mark",23,4000));		
		for(Object o: set){
			Employee e1=(Employee) o;
			System.out.println(e1.getName()+"---------"+e1.getAge()+"---------"+e1.getSalary());
		}
	
	}
	}
 
     
输出结果:
Mark---------23---------4000
Tom---------44---------5000
Amy---------24---------9000
可以看出,我们的元素通过薪水的从小到大的顺序打印出来,这也告诉我们了一个新的知识点,如果按照推荐规范重写compare方法时,调用比较器比较时,会以自然数的序列为准






你可能感兴趣的:(【学习笔记】)