Guava-libraries: Is the conflict Objects.hashCode (Object []) safe?

When looking at different override options, hashCode()I was directed to Objects.hashCode(Object[])in the Google guava libraries ( javadoc ). The javadoc states that it is delegating Arrays.hashCode(Object[]). Is it safe to use this method in many different types of objects? Isn't that prone to hash collisions, or is it unlikely just because containers usually only contain one type of object?

As a simple example, consider the following classes:

public class Student {
    private final String name;

    public Student(String name) {
        this.name = name;
    }

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

public class Teacher {
    private final String name;

    public Teacher(String name) {
        this.name = name;
    }

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

public class HashCodeDriver {
    public static void main(String[] args) {
        final String name = "moe";
        Student s = new Student(name);
        Teacher t = new Teacher(name);

        long studentHash = s.hashCode();
        long teacherHash = t.hashCode();
        System.out.println("studentHash=" + studentHash + " teacherHash=" + teacherHash);
        if(studentHash == teacherHash) {
            System.out.println("hash codes match");
        }
        else {
            System.out.println("hash codes don't match");
        }
    }
}

Conclusion:

studentHash=108322 teacherHash=108322
hash codes match

Objects are two different types, but they generate the same hash code. Isn't that a problem? Should I pass in the class as the first parameter to prevent this collision? For instance,

public class Student {
    private final String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(Student.class, name);
    }
}

public class Teacher {
    private final String name;

    public Teacher(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(Teacher.class, name);
    }
}

javadoc ? javadoc,

. , - - .

+5
3

, -.

, HashMap, . , HashMap<Object, Object>, ,

assertFalse( new Teacher( "John Smith" ).equals( new Student( "John Smith" ) );

hashCode, equals.

Arrays.hashCode(Object[]) , .

, - .

@Override
public int hashCode() {
    return name.hashCode();
}
+6

, x.hashCode() != Objects.hashCode(x) - . (, . . .)

hashCode/equals:

public class Teacher {
    private final String name;

    public Teacher(String name) {
        this.name = name;
    }

    @Override public equals(Object obj){
        if(obj == this) return true;
        if(!(obj instanceof Teacher)) return false;
        return Objects.equal(name, ((Teacher) obj).getName());
    }

    @Override public hashCode(){
        return 0;
    }
}

, - . hashCode() javadoc:

, equals (java.lang.Object), hashCode .

"" , . , HashMaps , .

:

@Override
public int hashCode() {
    return Objects.hashCode(Teacher.class, name);
}

( ), - . , .

, , hashCode ( ) . : n , n - . , .

+3

, Objects.hashCode() , xoring .

class Class1 {
  public int hashCode() {
    return Object.hashCode(...) ^ 0x12b7eff8;
  }
}

class Class2 {
  public int hashCode() {
    return Object.hashCode(...) ^ 0xe800792b;
  }
}

xoring , , , , , Object.hashCode .

. , - - .

javadoc ? javadoc,

. , . - - , singleValue.hashCode().

, , , -:

class Name {
  int cachedHashCode;

  ...
}

class Person {
  int cachedHashCode;  // 0 if not computed

  private final Name name;

  public boolean hasName(Name n) {
    return ((cachedHashCode != 0 && n.cachedHashCode != 0) 
            && cachedHashCode == n.cachedHashCode)
        || n.equals(name);
  }

  public int hashCode() {
    if (cachedHashCode == 0) { cachedHashCode = Object.hashCode(name); }
    return cachedHashCode;
  }
}
0

All Articles