可变对象和hashCode

rob*_*ert 21 java hashcode immutability hashset

有以下课程:

public class Member {
private int x;
private long y;
private double d;

public Member(int x, long y, double d) {
    this.x = x;
    this.y = y;
    this.d = d;
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + x;
    result = (int) (prime * result + y);
    result = (int) (prime * result + Double.doubleToLongBits(d));
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj instanceof Member) {
        Member other = (Member) obj;
        return other.x == x && other.y == y
                && Double.compare(d, other.d) == 0;
    }
    return false;
}

public static void main(String[] args) {
    Set<Member> test = new HashSet<Member>();
    Member b = new Member(1, 2, 3);
    test.add(b);
    System.out.println(b.hashCode());
    b.x = 0;
    System.out.println(b.hashCode());
    Member first = test.iterator().next();
    System.out.println(test.contains(first));
    System.out.println(b.equals(first));
           System.out.println(test.add(first));

}
Run Code Online (Sandbox Code Playgroud)

}

它产生以下结果:
30814 29853 false true true

因为hashCode取决于对象的状态,所以不能再正确检索它,因此检查包含失败.HashSet不再正常工作.解决方案是使成员不可变,但这是唯一的解决方案吗?是否所有添加到HashSet的类都是不可变的?有没有其他方法来处理这种情况?

问候.

Jon*_*eet 32

在hashsets对象应该要么是不可变的,或者你需要他们已经在一个HashSet(或HashMap的),使用后不改变他们行使纪律.

在实践中,我很少发现这是一个问题 - 我很少发现自己需要使用复杂的对象,因为键是设置元素,而当我这样做时,通常不是一个问题,只是不要改变它们.当然,如果你此时已经公开了对其他代码的引用,那么它会变得更难.

  • @Brian:我怀疑Tom正在谈论不覆盖hashCode或equals的类型. (3认同)

Osc*_*Ryz 9

是.在维护类可变的同时,可以根据类的不可变值(可能是生成的id)计算hashCode和equals方法,以遵守Object类中定义的hashCode契约:

  • 每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象上的equals比较中使用的信息.从应用程序的一次执行到同一应用程序的另一次执行,该整数不需要保持一致.

  • 如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须生成相同的整数结果.

  • 如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须生成不同的整数结果.但是,程序员应该知道为不等对象生成不同的整数结果可能会提高哈希表的性能.

根据您的情况,这可能更容易或不容易.

class Member { 
    private static long id = 0;

    private long id = Member.id++;
    // other members here... 


    public int hashCode() { return this.id; }
    public boolean equals( Object o ) { 
        if( this == o ) { return true; }
        if( o instanceOf Member ) { return this.id == ((Member)o).id; }
        return false;
     }
     ...
 }
Run Code Online (Sandbox Code Playgroud)

如果你需要一个线程安全属性,你可以考虑使用:AtomicLong,但同样,它取决于你将如何使用你的对象.