package Oct2016;
import java.util.SortedSet;
import java.util.TreeSet;
public class Ques1031 {
public static void main(String[] args) {
SortedSet<People> set = new TreeSet<People>();
set.add(new People(170));
set.add(new People(165));
for (People people : set) {
System.out.println("身高:" + people.getHeight());
}
System.out.println();
// 將身高矮的人變高
set.first().setHeight(175);
for (People people : set) {
System.out.println("身高:" + people.getHeight());
}
}
}
class People implements Comparable<People> {
private int height;
public People(int height) { this.height = height; }
public int getHeight() { return height; }
public void setHeight(int height) { this.height = height; }
@Override
public int compareTo(People p) {
return height - p.height; // 按身高升序
}
}
今日問題:請問主程序輸出結果是?
關鍵點: TreeSet 內部基於紅黑樹實現,依賴元素的 compareTo 方法維持順序。一旦元素加入集合,絕不能修改影響比較結果的屬性,否則會破壞樹的結構不變量,導致不可預期的行為。
身高:165 身高:170
正常按升序輸出。
set.first().setHeight(175); // 將最小元素(165)改為 175
此時集合中有兩個元素:原本 170 的節點,和被修改為 175 的節點。但紅黑樹結構並未重新調整,因為 TreeSet 不知道元素內部狀態已變。
身高:170 身高:175
看似正常,但這是巧合。實際上樹結構已損壞:
// 繼續測試 set.add(new People(168)); // 嘗試添加中間值 for (People p : set) System.out.println(p.getHeight()); // 可能輸出:170, 175, 168 (順序錯誤) 或拋出異常
TreeSet/TreeMap 鍵的對象應設計為不可變remove 再修改屬性,最後 add 回去compareTo 中可加入狀態檢查,但無法根本解決// 正確做法 People shortest = set.first(); set.remove(shortest); // 先移除 shortest.setHeight(175); // 修改屬性 set.add(shortest); // 重新加入,樹自動調整
題目來源:【Java每日一題】20161031,歡迎關注公眾號交流進步。