Applying Joerg's final patch from COLLECTIONS-266, including the unit test that shows the problem and fixes the problem by making the hashcode transient, and moving the hashcode implementation such that it can be called from the deserialization as well as the hashcode method

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@661577 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Henri Yandell 2008-05-30 06:23:45 +00:00
parent 7873bd62ee
commit d887c5913f
2 changed files with 91 additions and 8 deletions

View File

@ -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;
}
}

View File

@ -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));
}
}