集合與映射中所存物件屬性改變的問題

来源:1-8 更多哈希冲突的处理方法

慕用8103342

2021-12-01 17:01:45

講師你好:

若我將物件存儲到TreeSet , TreeMap, HashMap(key部分) 底層為樹的容器中,因為節點保存的是引用數據類型,也就是pointer,若是在後續的代碼中變更所儲存物件中的屬性,此時的樹會自動調整其結構以滿足二分搜索樹(紅黑樹)的性質? (物件屬性改變hashcode也會更動數值?) 還是集合或映射(Key值部分)所存儲的數據是要滿足不可改變的條件。

写回答

1回答

liuyubobobo

2021-12-01

非常非常好的问题。


在使用 TreeSet,TreeMap 或者 HashMap 的时候,开发者不应该去 update key。但是,从程序的角度,TreeSet,TreeMap 以及 HashMap 没有阻止开发者这么做。所以语法上你可以这么做,但是逻辑上不应该这样做。这就好像语法上,设计一个类,你可以让其 compareTo 等于 0 的逻辑和 equals 的逻辑不等价,但是你不应该这样做。


原因就是你说的,这些容器类不会因为你对 key 的 update,而自动调整结构(HashMap 也不会自动更新哈希值)。


正确的做法是,如果你想 update 一个 key,应该先删除原先的 key 对应的数据,再把新的 key 相应的数据重新添加进去。这不会导致复杂度的改变,虽然进行了两次操作。


==========


要想验证这一点,使用哈希表是非常容易的。


可以试验一下下面的代码:


public class Student {
    
    private String name;
    
    public Student(String name){
        this.name = name;
    }
    
    @Override
    public boolean equals(Object student){
        if(this == student) return true;
        if(student == null) return false;
        if(this.getClass() != student.getClass()) return false;
        Student another = (Student)student;
        return this.name.equals(another.name);
    }
    
    @Override
    public int hashCode(){
        return name.hashCode();
    }
    
    @Override
    public String toString(){
        return "Student : " + name;
    }
    
    public static void main(String[] args) {
        
        HashMap<Student, Integer> map = new HashMap<>();
        
        Student a = new Student("A");
        Student b = new Student("B");
        
        map.put(a, 1);
        map.put(b, 2);
        
        a.name += "A";
        
        Student aa = new Student("AA");
        map.put(aa, 3);
        
        map.forEach((k, v) -> System.out.println("key : " + k + " value : " + v));
        System.out.println();
    }
}


学生类,只有 name 属性,哈希值就是学生名称 name 这个字符串的哈希值。


在 main 中,我先把 name = "A" 的 Student 和 name = "B" 的 Student 添加进哈希表。然后,将 name = "A" 的学生名字改成 "AA"。


现在,我再添加另外一个叫 "AA" 的学生,理论上,哈希表中有两个叫 "AA" 的学生,后一个学生应该覆盖前一个学生。但是,这个操作后,打印哈希表,有三项,其中有两个叫 "AA" 的学生存在在哈希表中。


==========


为什么?因为第一个学生存在了哈希值是 "A" 的 bucket 下。改变这个学生的名字,哈希值不会动态调整,依然在 "A" 的 bucket 下。来了一个叫 "AA" 的学生,和 "A" 的哈希值不产生哈希冲突,所以,直接存在了 "AA" 的 bucket 下。于是,两个叫 "AA" 的学生存在了。


所以,你也可以看到,这相当于引入了 bug。因为 Map 中的数据,key 不应该相等,但通过这个过程,Map 中存在了 key 相等的数据。所以这种操作是应该避免的。


继续加油!:)

4

算法与数据结构

波波老师5年集大成之作,算法与数据结构系统学习,考试、面试、竞赛通用

2636 学习 · 1090 问题

查看课程