Author: dlaha Date: Sat Jul 12 21:10:23 2014 New Revision: 1610049 URL: http://svn.apache.org/r1610049 Log: [COLLECTIONS-508] Updating equals and hashCode for ListValuedMap, SetValuedMap and MultiValuedMap
Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java?rev=1610049&r1=1610048&r2=1610049&view=diff ============================================================================== --- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java (original) +++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java Sat Jul 12 21:10:23 2014 @@ -18,9 +18,12 @@ package org.apache.commons.collections4. import java.io.Serializable; import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Map.Entry; import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.ListValuedMap; @@ -97,6 +100,49 @@ public abstract class AbstractListValued return ListUtils.emptyIfNull((List<V>) getMap().remove(key)); } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof ListValuedMap == false) { + return false; + } + ListValuedMap<?, ?> other = (ListValuedMap<?, ?>) obj; + if (other.size() != size()) { + return false; + } + Iterator<?> it = keySet().iterator(); + while (it.hasNext()) { + Object key = it.next(); + List<?> list = get(key); + List<?> otherList = other.get(key); + if (otherList == null) { + return false; + } + if (ListUtils.isEqualList(list, otherList) == false) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int h = 0; + Iterator<Entry<K, Collection<V>>> it = getMap().entrySet().iterator(); + while (it.hasNext()) { + Entry<K, Collection<V>> entry = it.next(); + K key = entry.getKey(); + List<V> valueList = (List<V>) entry.getValue(); + h += (key == null ? 0 : key.hashCode()) ^ ListUtils.hashCodeForList(valueList); + } + return h; + } + /** * Wrapped list to handle add and remove on the list returned by get(object) */ @@ -173,6 +219,34 @@ public abstract class AbstractListValued return list.subList(fromIndex, toIndex); } + @Override + public boolean equals(Object other) { + final List<V> list = (List<V>) getMapping(); + if (list == null) { + return Collections.emptyList().equals(other); + } + if (other == null) { + return false; + } + if (!(other instanceof List)) { + return false; + } + List<?> otherList = (List<?>) other; + if (ListUtils.isEqualList(list, otherList) == false) { + return false; + } + return true; + } + + @Override + public int hashCode() { + final List<V> list = (List<V>) getMapping(); + if (list == null) { + return Collections.emptyList().hashCode(); + } + return ListUtils.hashCodeForList(list); + } + } /** Values ListItrerator */ Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java?rev=1610049&r1=1610048&r2=1610049&view=diff ============================================================================== --- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java (original) +++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java Sat Jul 12 21:10:23 2014 @@ -396,7 +396,6 @@ public class AbstractMultiValuedMap<K, V return new MultiValuedMapIterator(); } - @SuppressWarnings("rawtypes") @Override public boolean equals(Object obj) { if (this == obj) { @@ -412,7 +411,7 @@ public class AbstractMultiValuedMap<K, V if (other.size() != size()) { return false; } - Iterator it = keySet().iterator(); + Iterator<?> it = keySet().iterator(); while (it.hasNext()) { Object key = it.next(); Collection<?> col = get(key); @@ -420,21 +419,34 @@ public class AbstractMultiValuedMap<K, V if (otherCol == null) { return false; } - if (col.size() != otherCol.size()) { + if (CollectionUtils.isEqualCollection(col, otherCol) == false) { return false; } - for (Object value : col) { - if (!otherCol.contains(value)) { - return false; - } - } } return true; } @Override public int hashCode() { - return getMap().hashCode(); + int h = 0; + Iterator<Entry<K, Collection<V>>> it = getMap().entrySet().iterator(); + while (it.hasNext()) { + Entry<K, Collection<V>> entry = it.next(); + K key = entry.getKey(); + Collection<V> valueCol = entry.getValue(); + int vh = 0; + if (valueCol != null) { + Iterator<V> colIt = valueCol.iterator(); + while (colIt.hasNext()) { + V val = colIt.next(); + if (val != null) { + vh += val.hashCode(); + } + } + } + h += (key == null ? 0 : key.hashCode()) ^ vh; + } + return h; } @Override @@ -588,7 +600,6 @@ public class AbstractMultiValuedMap<K, V return col.toArray(a); } - @SuppressWarnings("rawtypes") @Override public boolean equals(Object other) { final Collection<V> col = getMapping(); @@ -601,15 +612,10 @@ public class AbstractMultiValuedMap<K, V if(!(other instanceof Collection)){ return false; } - Collection otherCol = (Collection) other; - if (col.size() != otherCol.size()) { + Collection<?> otherCol = (Collection<?>) other; + if (CollectionUtils.isEqualCollection(col, otherCol) == false) { return false; } - for (Object value : col) { - if (!otherCol.contains(value)) { - return false; - } - } return true; } @@ -619,7 +625,15 @@ public class AbstractMultiValuedMap<K, V if (col == null) { return CollectionUtils.EMPTY_COLLECTION.hashCode(); } - return col.hashCode(); + int h = 0; + Iterator<V> it = col.iterator(); + while (it.hasNext()) { + V val = it.next(); + if (val != null) { + h += val.hashCode(); + } + } + return h; } @Override Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java?rev=1610049&r1=1610048&r2=1610049&view=diff ============================================================================== --- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java (original) +++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java Sat Jul 12 21:10:23 2014 @@ -16,8 +16,12 @@ */ package org.apache.commons.collections4.multimap; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.Map.Entry; import org.apache.commons.collections4.SetUtils; import org.apache.commons.collections4.SetValuedMap; @@ -93,6 +97,49 @@ public abstract class AbstractSetValuedM return SetUtils.emptyIfNull((Set<V>) getMap().remove(key)); } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof SetValuedMap == false) { + return false; + } + SetValuedMap<?, ?> other = (SetValuedMap<?, ?>) obj; + if (other.size() != size()) { + return false; + } + Iterator<?> it = keySet().iterator(); + while (it.hasNext()) { + Object key = it.next(); + Set<?> set = get(key); + Set<?> otherSet = other.get(key); + if (otherSet == null) { + return false; + } + if (SetUtils.isEqualSet(set, otherSet) == false) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int h = 0; + Iterator<Entry<K, Collection<V>>> it = getMap().entrySet().iterator(); + while (it.hasNext()) { + Entry<K, Collection<V>> entry = it.next(); + K key = entry.getKey(); + Set<V> valueSet = (Set<V>) entry.getValue(); + h += (key == null ? 0 : key.hashCode()) ^ SetUtils.hashCodeForSet(valueSet); + } + return h; + } + /** * Wrapped set to handle add and remove on the collection returned by * get(object) @@ -103,6 +150,34 @@ public abstract class AbstractSetValuedM super(key); } + @Override + public boolean equals(Object other) { + final Set<V> set = (Set<V>) getMapping(); + if (set == null) { + return Collections.emptySet().equals(other); + } + if (other == null) { + return false; + } + if (!(other instanceof Set)) { + return false; + } + Set<?> otherSet = (Set<?>) other; + if (SetUtils.isEqualSet(set, otherSet) == false) { + return false; + } + return true; + } + + @Override + public int hashCode() { + final Set<V> set = (Set<V>) getMapping(); + if (set == null) { + return Collections.emptySet().hashCode(); + } + return SetUtils.hashCodeForSet(set); + } + } } Modified: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java?rev=1610049&r1=1610048&r2=1610049&view=diff ============================================================================== --- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java (original) +++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java Sat Jul 12 21:10:23 2014 @@ -163,6 +163,64 @@ public class MultiValuedHashMapTest<K, V assertEquals(2, listMap.get("B").size()); } + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void testEqualsHashCodeContract() { + MultiValuedMap map1 = new MultiValuedHashMap(); + MultiValuedMap map2 = new MultiValuedHashMap(); + + map1.put("a", "a1"); + map1.put("a", "a2"); + map2.put("a", "a2"); + map2.put("a", "a1"); + assertEquals(map1, map2); + assertEquals(map1.hashCode(), map2.hashCode()); + + map2.put("a", "a2"); + assertNotSame(map1, map2); + assertNotSame(map1.hashCode(), map2.hashCode()); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void testListValuedMapEqualsHashCodeContract() { + ListValuedMap map1 = MultiValuedHashMap.listValuedHashMap(); + ListValuedMap map2 = MultiValuedHashMap.listValuedHashMap(); + + map1.put("a", "a1"); + map1.put("a", "a2"); + map2.put("a", "a1"); + map2.put("a", "a2"); + assertEquals(map1, map2); + assertEquals(map1.hashCode(), map2.hashCode()); + + map1.put("b", "b1"); + map1.put("b", "b2"); + map2.put("b", "b2"); + map2.put("b", "b1"); + assertNotSame(map1, map2); + assertNotSame(map1.hashCode(), map2.hashCode()); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void testSetValuedMapEqualsHashCodeContract() { + SetValuedMap map1 = MultiValuedHashMap.setValuedHashMap(); + SetValuedMap map2 = MultiValuedHashMap.setValuedHashMap(); + + map1.put("a", "a1"); + map1.put("a", "a2"); + map2.put("a", "a2"); + map2.put("a", "a1"); + assertEquals(map1, map2); + assertEquals(map1.hashCode(), map2.hashCode()); + + map2.put("a", "a2"); + assertEquals(map1, map2); + assertEquals(map1.hashCode(), map2.hashCode()); + + map2.put("a", "a3"); + assertNotSame(map1, map2); + assertNotSame(map1.hashCode(), map2.hashCode()); + } + // public void testCreate() throws Exception { // writeExternalFormToDisk((java.io.Serializable) makeObject(), // "src/test/resources/data/test/MultiValuedHashMap.emptyCollection.version4.1.obj");