Skip to content

Commit 0f0abef

Browse files
committed
[COLLECTIONS-887] ConcurrentReferenceHashMap.remove(key), remove(key,
value), replace(key, value), and replace(key, oldValue, newValue) throw NullPointerException inconsistently
1 parent 1009964 commit 0f0abef

3 files changed

Lines changed: 110 additions & 0 deletions

File tree

src/changes/changes.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
<action type="fix" dev="ggregory" due-to="Paul King, Gary Gregory, Sebb">Add MultiValuedMap.inverted() #665.</action>
4545
<action type="fix" dev="ggregory" due-to="John Griffin, Gary Gregory" issue="COLLECTIONS-885">PatriciaTrie submap and range view operations silently mutate trie structure resulting in ConcurrentModificationException for any iterating threads #672.</action>
4646
<action type="fix" dev="ggregory" due-to="Partha Protim Paul" issue="COLLECTIONS-886">[javadoc] CompositeSet.addComposited(Set) does not throw NullPointerException when null is passed #675.</action>
47+
<action type="fix" dev="ggregory" due-to="Partha Paul, Gary Gregory" issue="COLLECTIONS-887">ConcurrentReferenceHashMap.remove(key), remove(key, value), replace(key, value), and replace(key, oldValue, newValue) throw NullPointerException inconsistently
48+
.</action>
4749
<!-- ADD -->
4850
<action type="add" dev="ggregory" due-to="Gary Gregory">Add generics to UnmodifiableIterator for the wrapped type.</action>
4951
<action type="add" dev="ggregory" due-to="Gary Gregory">Add a Maven benchmark profile for JMH.</action>

src/main/java/org/apache/commons/collections4/map/ConcurrentReferenceHashMap.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,7 +1642,16 @@ public V get(final Object key) {
16421642
return segmentFor(hash).get(key, hash);
16431643
}
16441644

1645+
/**
1646+
* Returns the hash code of the given key, which is either the result of calling {@code hashCode} or {@code System.identityHashCode} depending on
1647+
* {@code identityComparisons}.
1648+
*
1649+
* @param key The key to hash.
1650+
* @return the hash code of the given key.
1651+
* @throws NullPointerException if the specified key is null.
1652+
*/
16451653
private int hashOf(final Object key) {
1654+
Objects.requireNonNull(key, "key");
16461655
return hash(identityComparisons ? System.identityHashCode(key) : key.hashCode());
16471656
}
16481657

src/test/java/org/apache/commons/collections4/map/AbstractConcurrentReferenceHashMapTest.java

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717

1818
package org.apache.commons.collections4.map;
1919

20+
import static org.junit.jupiter.api.Assertions.assertThrows;
21+
2022
import java.util.EnumSet;
2123

2224
import org.apache.commons.collections4.map.ConcurrentReferenceHashMap.Option;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Test;
2327

2428
/**
2529
* Tests {@link ConcurrentReferenceHashMap}.
@@ -31,6 +35,11 @@ public abstract class AbstractConcurrentReferenceHashMapTest<K, V> extends Abstr
3135

3236
protected static final EnumSet<Option> IDENTITY_COMPARISONS = EnumSet.of(Option.IDENTITY_COMPARISONS);
3337

38+
@BeforeEach
39+
void beforeEachFill() {
40+
resetFull();
41+
}
42+
3443
@Override
3544
public boolean isAllowNullKey() {
3645
return false;
@@ -51,4 +60,94 @@ public boolean isTestSerialization() {
5160
return false;
5261
}
5362

63+
@Test
64+
void testComputeNullKey() {
65+
final ConcurrentReferenceHashMap<K, V> map = getMap();
66+
assertThrows(NullPointerException.class, () -> map.compute(null, (k, v) -> getSampleValues()[0]));
67+
}
68+
69+
@Test
70+
void testComputeNullKeyIfAbsent() {
71+
final ConcurrentReferenceHashMap<K, V> map = getMap();
72+
assertThrows(NullPointerException.class, () -> map.computeIfAbsent(null, k -> getSampleValues()[0]));
73+
}
74+
75+
@Test
76+
void testComputeNullKeyIfPresent() {
77+
final ConcurrentReferenceHashMap<K, V> map = getMap();
78+
assertThrows(NullPointerException.class, () -> map.computeIfPresent(null, (k, v) -> getSampleValues()[0]));
79+
}
80+
81+
@Test
82+
void testContainsNullKey() {
83+
final ConcurrentReferenceHashMap<K, V> map = getMap();
84+
assertThrows(NullPointerException.class, () -> map.containsKey(null));
85+
}
86+
87+
@Test
88+
void testContainsNullValue() {
89+
final ConcurrentReferenceHashMap<K, V> map = getMap();
90+
assertThrows(NullPointerException.class, () -> map.containsValue(null));
91+
}
92+
93+
@Test
94+
void testGetNullKey() {
95+
final ConcurrentReferenceHashMap<K, V> map = getMap();
96+
assertThrows(NullPointerException.class, () -> map.get(null));
97+
}
98+
99+
@Test
100+
void testGetNullKeyOrDefault() {
101+
final ConcurrentReferenceHashMap<K, V> map = getMap();
102+
assertThrows(NullPointerException.class, () -> map.getOrDefault(null, getSampleValues()[0]));
103+
}
104+
105+
@Test
106+
void testMergeNullKey() {
107+
final ConcurrentReferenceHashMap<K, V> map = getMap();
108+
final V[] sampleValues = getSampleValues();
109+
assertThrows(NullPointerException.class, () -> map.merge(null, sampleValues[0], (k, v) -> sampleValues[0]));
110+
}
111+
112+
@Test
113+
void testMergeNullValue() {
114+
final ConcurrentReferenceHashMap<K, V> map = getMap();
115+
assertThrows(NullPointerException.class, () -> map.merge(getSampleKeys()[0], null, (k, v) -> null));
116+
map.merge(getSampleKeys()[0], getSampleValues()[0], (k, v) -> null);
117+
}
118+
119+
@Test
120+
void testPutNullKey() {
121+
final ConcurrentReferenceHashMap<K, V> map = getMap();
122+
final K key = null;
123+
assertThrows(NullPointerException.class, () -> map.put(key, getSampleValues()[0]));
124+
}
125+
126+
@Test
127+
void testPutNullValue() {
128+
final ConcurrentReferenceHashMap<K, V> map = getMap();
129+
assertThrows(NullPointerException.class, () -> map.put(getSampleKeys()[0], null));
130+
}
131+
132+
@Test
133+
void testRemoveNullKey() {
134+
final ConcurrentReferenceHashMap<K, V> map = getMap();
135+
final K key = null;
136+
final V[] values = getSampleValues();
137+
assertThrows(NullPointerException.class, () -> map.remove(key, values[0]));
138+
assertThrows(NullPointerException.class, () -> map.remove(key));
139+
assertThrows(NullPointerException.class, () -> map.replace(key, values[0]));
140+
assertThrows(NullPointerException.class, () -> map.replace(key, values[0], values[1]));
141+
}
142+
143+
@Test
144+
void testReplaceNullKey() {
145+
final ConcurrentReferenceHashMap<K, V> map = getMap();
146+
final K key = null;
147+
final V[] values = getSampleValues();
148+
assertThrows(NullPointerException.class, () -> map.remove(key, values[0]));
149+
assertThrows(NullPointerException.class, () -> map.remove(key));
150+
assertThrows(NullPointerException.class, () -> map.replace(key, values[0]));
151+
assertThrows(NullPointerException.class, () -> map.replace(key, values[0], values[1]));
152+
}
54153
}

0 commit comments

Comments
 (0)