diff --git a/src/java/org/apache/commons/collections/keyvalue/MultiKey.java b/src/java/org/apache/commons/collections/keyvalue/MultiKey.java index 5e35a8f6c..62b258db3 100644 --- a/src/java/org/apache/commons/collections/keyvalue/MultiKey.java +++ b/src/java/org/apache/commons/collections/keyvalue/MultiKey.java @@ -16,6 +16,8 @@ */ package org.apache.commons.collections.keyvalue; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Arrays; @@ -54,7 +56,7 @@ public class MultiKey implements Serializable { /** The individual keys */ private final Object[] keys; /** The cached hashCode */ - private final int hashCode; + private transient int hashCode; /** * Constructor taking two keys. @@ -164,13 +166,7 @@ public class MultiKey implements Serializable { this.keys = keys; } - int total = 0; - for (int i = 0; i < keys.length; i++) { - if (keys[i] != null) { - total ^= keys[i].hashCode(); - } - } - hashCode = total; + calculateHashCode(keys); } //----------------------------------------------------------------------- @@ -255,4 +251,29 @@ public class MultiKey implements Serializable { return "MultiKey" + Arrays.asList(keys).toString(); } + /** + * Calculate the hash code of the instance using the provided keys. + * @param keys + */ + private void calculateHashCode(Object[] keys) + { + int total = 0; + for (int i = 0; i < keys.length; i++) { + if (keys[i] != null) { + total ^= keys[i].hashCode(); + } + } + hashCode = total; + } + + /** + * Recalculate the hash code after deserialization. The hash code of some + * keys might have change (hash codes based on the system hash code are + * only stable for the same process). + * @return the instance with recalculated hash code + */ + private Object readResolve() { + calculateHashCode(keys); + return this; + } } diff --git a/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java b/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java index 316a784de..44ece5a3e 100644 --- a/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java +++ b/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java @@ -16,7 +16,15 @@ */ package org.apache.commons.collections.keyvalue; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import junit.framework.Assert; import junit.framework.Test; @@ -206,4 +214,58 @@ public class TestMultiKey extends TestCase { Assert.assertTrue(mk1.equals(null) == false); } + static class SystemHashCodeSimulatingKey implements Serializable { + + private static final long serialVersionUID = 1L; + private final String name; + private int hashCode = 1; + + public SystemHashCodeSimulatingKey(String name) + { + this.name = name; + } + + public boolean equals(Object obj) + { + return obj instanceof SystemHashCodeSimulatingKey + && name.equals(((SystemHashCodeSimulatingKey)obj).name); + } + + public int hashCode() + { + return hashCode; + } + + private Object readResolve() { + hashCode=2; // simulate different hashCode after deserialization in another process + return this; + } + } + + public void testEqualsAfterSerialization() throws IOException, ClassNotFoundException + { + SystemHashCodeSimulatingKey sysKey = new SystemHashCodeSimulatingKey("test"); + MultiKey mk = new MultiKey(ONE, sysKey); + Map map = new HashMap(); + map.put(mk, TWO); + + // serialize + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(sysKey); + out.writeObject(map); + out.close(); + + // deserialize + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + sysKey = (SystemHashCodeSimulatingKey)in.readObject(); // simulate deserialization in another process + Map map2 = (Map) in.readObject(); + in.close(); + + assertEquals(2, sysKey.hashCode()); // different hashCode now + + MultiKey mk2 = new MultiKey(ONE, sysKey); + assertEquals(TWO, map2.get(mk2)); + } }