1. 程式人生 > >TreeSet有趣問題之add方法原理

TreeSet有趣問題之add方法原理

先看如下程式碼

class Worker implements Comparable<Worker> {
	private int age;
	private String name;

	public Worker(int age, String name) {
		this.setAge(age);
		this.setName(name);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Worker other = (Worker) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public int compareTo(Worker worker) {
		return new Integer(getAge()).compareTo(new Integer(worker.getAge()));
	}

	...//setter/getter方法

}
public static void main(String[] args) {
<span style="white-space:pre">	</span>TreeSet<Worker> set = new TreeSet<Worker>();
	set.add(new Worker(21, "測試人員1"));
	set.add(new Worker(22,"測試人員2"));
	set.add(new Worker(21, "測試人員3"));
	System.out.println(set.size());
}
輸出的結果是幾呢?

按照寫程式碼者的原意應該是想set裡面有三個worker,按照年齡排序的.

可是執行出來的結果卻是2,再細檢視會發現測試人員1沒有了,只有2和3.

這是為什麼呢?檢視原始碼,發現TreeSet的add方法定義如下:

 public boolean add(E paramE)
  {
    return this.m.put(paramE, PRESENT) == null;
  }
其中this.m為TreeMap,其put方法定義如下:
public V put(K paramK, V paramV)
  {
    Entry localEntry1 = this.root;
    if (localEntry1 == null)
    {
      compare(paramK, paramK);
      this.root = new Entry(paramK, paramV, null);
      this.size = 1;
      this.modCount += 1;
      return null;
    }
    Comparator localComparator = this.comparator;
    Entry localEntry2;
    int i;
    if (localComparator != null)
    {
      do
      {
        <span style="color:#ff0000;">localEntry2 = localEntry1;
        i = localComparator.compare(paramK, localEntry1.key);
        if (i < 0) {
          localEntry1 = localEntry1.left;
        } else if (i > 0) {
          localEntry1 = localEntry1.right;
        } else {
          return localEntry1.setValue(paramV);
        }</span>
      } while (localEntry1 != null);
    }
    else
    {
      if (paramK == null) {
        throw new NullPointerException();
      }
      localObject = (Comparable)paramK;
      do
      {
        <span style="color:#ff0000;">localEntry2 = localEntry1;
        i = ((Comparable)localObject).compareTo(localEntry1.key);
        if (i < 0) {
          localEntry1 = localEntry1.left;
        } else if (i > 0) {
          localEntry1 = localEntry1.right;
        } else {
          return localEntry1.setValue(paramV);
        }</span>
      } while (localEntry1 != null);
    }
    Object localObject = new Entry(paramK, paramV, localEntry2);
    if (i < 0) {
      localEntry2.left = ((Entry)localObject);
    } else {
      localEntry2.right = ((Entry)localObject);
    }
    fixAfterInsertion((Entry)localObject);
    this.size += 1;
    this.modCount += 1;
    return null;
  }
如上紅色程式碼,原來在TreeSet的add方法中根本就沒有使用equals方法來判斷兩個元素是否相等,而是直接使用compareTo方法來確定新增元素在集合中的位置.

若要實現我們想要的結果,Worker中的compareTo方法應該做修改為

@Override
public int compareTo(Worker worker) {
	int index = getName().compareTo(worker.getName());
	if(index != 0){
		index = new Integer(getAge()).compareTo(new Integer(worker.getAge()));
	}
	return index;
}
如此就能得到我們想要的結果了

結論:TreeSet中確定集合中兩元素相等不是使用equals方法,而是使用compareTo方法.