[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:
parent
0a7871a141
commit
b0580d508b
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue