险象环生之set remove注意事项

问题


public class PatternTest {

    public static void main(String[] args) {
        Set<Person> set = new HashSet<>();
        Person p1 = new Person(1,"张三");
        set.add(p1);
        Person p2 = new Person(2,"李四");
        set.add(p2);
        System.out.println("1:"+p1.hashCode());
        p1.name = "张三三0";
        System.out.println("2:"+p1.hashCode());
        set.removeIf(item->{
            System.out.println("3:"+item.hashCode()+"  "+item.name);
            return item.id  == 1;
        });
        System.out.println(set.size());

    }

}

class Person{
    int id;
    String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

上述代码中执行remove后无法删除set中id为1的person

原因

HashSet是根据hashcode方法定位数组索引的,当修改person.name后,person的hashcode值会发生变化,即该对应在数组中的索引与之前不同了,再remove重新计算hashcode时,删除另一个索引上的链表中数据,即无法删除原对象

该问题更多的出现在 Person被标注了 @Data的情况下,lomok现在使用越来越多,在@Data标注时,会根据所有的字段重写 hashcode及equal