天天看點

Java TreeSet的"陷阱"

    TreeSet基于TreeMap,它很有意思,當你執行add()方法的時候,集合會自動排序,不需要手工執行Collections.sort()進行排序,代碼更加簡潔(Clean),今天使用過程發現它暗藏機關,與大家分享。

使用TreeSet有兩種方法:

一、元素實作Comparable接口,然後直接通過add方法添加元素即可。

二、建立一個Comparator,并在構造TreeSet的時候傳入,如下:

//我這裡Job為待添加元素

Set<Job> jobs = new TreeSet<Job>(new JobComparator());

我要實作的需求很簡單,根據Job的優先級字段(Priority)進行排序,Job類代碼如下:

public class Job implements Comparable<SortJob> {

	private String jobName;

	private Integer priority;

	public SortJob() {
	}

	public SortJob(String jobName, Integer priority) {
		this.jobName = jobName;
		this.priority = priority;
	}

	public String getJobName() {
		return jobName;
	}

	public void setJobName(String jobName) {
		this.jobName = jobName;
	}

	public Integer getPriority() {
		return priority;
	}

	public void setPriority(Integer priority) {
		this.priority = priority;
	}

	@Override
	public int compareTo(SortJob o) {
		return priority.compareTo(o.getPriority());
	}
}
           

   測試代碼如下:

@Test
	public void sortJob() {
		Set<SortJob> jobs = new TreeSet<SortJob>();

		jobs.add(new SortJob("001", 1));
		jobs.add(new SortJob("002", 1));
		jobs.add(new SortJob("003", 1));
		assertEquals(3, jobs.size());    // ①

		jobs.clear();
		jobs.add(new SortJob("001", 1));
		jobs.add(new SortJob("002", 3));
		jobs.add(new SortJob("003", 2));

		List<SortJob> listJobs = new ArrayList<SortJob>(jobs);
		assertEquals("001", listJobs.get(0).getJobName());
		assertEquals("003", listJobs.get(1).getJobName());
		assertEquals("002", listJobs.get(2).getJobName());
	}
           

    測試并沒有通過,在行①,這裡實際集合size為1,出了什麼問題呢?

    soga,原來TreeSet的add方法會先調用Job的compareTo方法判斷元素是否重複,因為Priority全部為1,是以TreeSet認為後兩個Job是重複的,這樣的結果顯然不是我們想要的,重新實作compareTo方法如下:

@Override
	public int compareTo(SortJob o) {
		int compare1 = priority.compareTo(o.getPriority());
		return compare1 == 0 ? this.jobName.compareTo(o.jobName) : compare1;
	}
           

再次運作測試,OK了。

總結:

  1. TreeSet通過集合元素的compareTo方法判斷元素是否重複,實作compareTo方法既要考慮排序,又要考慮對象是否重複
  2. 發現這個陷阱後,發現Collections.sort()還是很不錯的選擇,隻需要考慮排序就可以了。

--heipark