首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 每日一題:TreeSet 修改元素屬性導致順序錯亂

     編輯:Java 綜合教學

題目代碼

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

看似正常,但這是巧合。實際上樹結構已損壞:

  • 原根節點可能是 165,左子樹為空,右子樹為 170
  • 修改後根節點值變為 175,但右子節點仍為 170,違反 BST 性質(左<根<右)
  • 後續操作(如 add、remove、contains)會出現邏輯錯誤

驗證代碼

// 繼續測試
set.add(new People(168)); // 嘗試添加中間值
for (People p : set) System.out.println(p.getHeight());
// 可能輸出:170, 175, 168 (順序錯誤) 或拋出異常

最佳實踐

  1. 不可變對象:作為 TreeSet/TreeMap 鍵的對象應設計為不可變
  2. 刪除重插:需修改排序屬性時,先 remove 再修改屬性,最後 add 回去
  3. 防禦性編程:compareTo 中可加入狀態檢查,但無法根本解決

正確修改方式

// 正確做法
People shortest = set.first();
set.remove(shortest);      // 先移除
shortest.setHeight(175);   // 修改屬性
set.add(shortest);         // 重新加入,樹自動調整

題目來源:【Java每日一題】20161031,歡迎關注公眾號交流進步。

公眾號二維碼

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved