[COLLECTIONS-508] Updating equals and hashCode for ListValuedMap, SetValuedMap and MultiValuedMap

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1610049 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
dlaha 2014-07-12 21:10:23 +00:00
parent 0a7871a141
commit b0580d508b
4 changed files with 239 additions and 18 deletions

View File

@ -18,9 +18,12 @@ package org.apache.commons.collections4.multimap;
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 AbstractListValuedMap<K, V> extends AbstractMultiValuedMap
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 AbstractListValuedMap<K, V> extends AbstractMultiValuedMap
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 */

View File

@ -396,7 +396,6 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
return new MultiValuedMapIterator();
}
@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object obj) {
if (this == obj) {
@ -412,7 +411,7 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
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> implements MultiValuedMap<K, V>, Seria
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> implements MultiValuedMap<K, V>, Seria
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> implements MultiValuedMap<K, V>, Seria
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> implements MultiValuedMap<K, V>, Seria
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

View File

@ -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 AbstractSetValuedMap<K, V> extends AbstractMultiValuedMap<
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 AbstractSetValuedMap<K, V> extends AbstractMultiValuedMap<
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);
}
}
}

View File

@ -163,6 +163,64 @@ public class MultiValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest<K,
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");