Backport COLLECTIONS-266 to 3.2.2.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/branches/COLLECTIONS_3_2_X@1713176 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2015-11-07 20:53:57 +00:00
parent 3afb3cb345
commit fd1790522e
3 changed files with 86 additions and 13 deletions

View File

@ -26,6 +26,9 @@
<action issue="COLLECTIONS-335" dev="jochen" type="fix" due-to="sebb"> <action issue="COLLECTIONS-335" dev="jochen" type="fix" due-to="sebb">
Fixed cache assignment for "TreeBidiMap#entrySet". Fixed cache assignment for "TreeBidiMap#entrySet".
</action> </action>
<action issue="COLLECTIONS-266" dev="bayard" type="fix" due-to="Joerg Schaible">
"MultiKey" will now be correctly serialized/de-serialized.
</action>
<action issue="COLLECTIONS-249" dev="bayard" type="fix" due-to="Joe Kelly"> <action issue="COLLECTIONS-249" dev="bayard" type="fix" due-to="Joe Kelly">
"SetUniqueList.addAll(int, Collection)" now correctly add the collection at the "SetUniqueList.addAll(int, Collection)" now correctly add the collection at the
provided index. provided index.
@ -75,9 +78,6 @@
"CaseInsensitiveMap" will now convert input strings to lower-case in a "CaseInsensitiveMap" will now convert input strings to lower-case in a
locale-independant manner. locale-independant manner.
</action> </action>
<action issue="COLLECTIONS-266" dev="bayard" type="fix" due-to="Joerg Schaible">
"MultiKey" will now be correctly serialized/de-serialized.
</action>
<action issue="COLLECTIONS-261" dev="bayard" type="fix" due-to="ori"> <action issue="COLLECTIONS-261" dev="bayard" type="fix" due-to="ori">
"Flat3Map#remove(Object)" will now return the correct value mapped to the removed key "Flat3Map#remove(Object)" will now return the correct value mapped to the removed key
if the size of the map is less or equal 3. if the size of the map is less or equal 3.

View File

@ -54,7 +54,7 @@ public class MultiKey implements Serializable {
/** The individual keys */ /** The individual keys */
private final Object[] keys; private final Object[] keys;
/** The cached hashCode */ /** The cached hashCode */
private final int hashCode; private transient int hashCode;
/** /**
* Constructor taking two keys. * Constructor taking two keys.
@ -163,14 +163,8 @@ public class MultiKey implements Serializable {
} else { } else {
this.keys = keys; this.keys = keys;
} }
int total = 0; calculateHashCode(keys);
for (int i = 0; i < keys.length; i++) {
if (keys[i] != null) {
total ^= keys[i].hashCode();
}
}
hashCode = total;
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -255,4 +249,30 @@ public class MultiKey implements Serializable {
return "MultiKey" + Arrays.asList(keys).toString(); 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; 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.Arrays;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Assert; import junit.framework.Assert;
import junit.framework.Test; import junit.framework.Test;
@ -205,5 +213,50 @@ public class TestMultiKey extends TestCase {
Assert.assertTrue(mk1.equals("") == false); Assert.assertTrue(mk1.equals("") == false);
Assert.assertTrue(mk1.equals(null) == false); 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));
}
} }