Skip to content

Commit 356ef45

Browse files
committed
ConcurrentReferenceHashMap properly handles getOrDefault for null values
Issue: SPR-16584
1 parent cc12afd commit 356ef45

File tree

2 files changed

+96
-86
lines changed

2 files changed

+96
-86
lines changed

spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java

+38-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -54,6 +54,7 @@
5454
* {@linkplain SoftReference soft entry references}.
5555
*
5656
* @author Phillip Webb
57+
* @author Juergen Hoeller
5758
* @since 3.2
5859
* @param <K> the key type
5960
* @param <V> the value type
@@ -226,19 +227,30 @@ protected int getHash(@Nullable Object o) {
226227

227228
@Override
228229
@Nullable
229-
public V get(Object key) {
230-
Reference<K, V> reference = getReference(key, Restructure.WHEN_NECESSARY);
231-
Entry<K, V> entry = (reference != null ? reference.get() : null);
230+
public V get(@Nullable Object key) {
231+
Entry<K, V> entry = getEntryIfAvailable(key);
232232
return (entry != null ? entry.getValue() : null);
233233
}
234234

235235
@Override
236-
public boolean containsKey(Object key) {
237-
Reference<K, V> reference = getReference(key, Restructure.WHEN_NECESSARY);
238-
Entry<K, V> entry = (reference != null ? reference.get() : null);
236+
@Nullable
237+
public V getOrDefault(@Nullable Object key, @Nullable V defaultValue) {
238+
Entry<K, V> entry = getEntryIfAvailable(key);
239+
return (entry != null ? entry.getValue() : defaultValue);
240+
}
241+
242+
@Override
243+
public boolean containsKey(@Nullable Object key) {
244+
Entry<K, V> entry = getEntryIfAvailable(key);
239245
return (entry != null && ObjectUtils.nullSafeEquals(entry.getKey(), key));
240246
}
241247

248+
@Nullable
249+
private Entry<K, V> getEntryIfAvailable(@Nullable Object key) {
250+
Reference<K, V> reference = getReference(key, Restructure.WHEN_NECESSARY);
251+
return (reference != null ? reference.get() : null);
252+
}
253+
242254
/**
243255
* Return a {@link Reference} to the {@link Entry} for the specified {@code key},
244256
* or {@code null} if not found.
@@ -254,28 +266,28 @@ protected final Reference<K, V> getReference(@Nullable Object key, Restructure r
254266

255267
@Override
256268
@Nullable
257-
public V put(K key, V value) {
269+
public V put(@Nullable K key, @Nullable V value) {
258270
return put(key, value, true);
259271
}
260272

261273
@Override
262274
@Nullable
263-
public V putIfAbsent(K key, V value) {
275+
public V putIfAbsent(@Nullable K key, @Nullable V value) {
264276
return put(key, value, false);
265277
}
266278

267279
@Nullable
268-
private V put(final K key, final V value, final boolean overwriteExisting) {
280+
private V put(@Nullable final K key, @Nullable final V value, final boolean overwriteExisting) {
269281
return doTask(key, new Task<V>(TaskOption.RESTRUCTURE_BEFORE, TaskOption.RESIZE) {
270282
@Override
271283
@Nullable
272284
protected V execute(@Nullable Reference<K, V> reference, @Nullable Entry<K, V> entry, @Nullable Entries entries) {
273285
if (entry != null) {
274-
V previousValue = entry.getValue();
286+
V oldValue = entry.getValue();
275287
if (overwriteExisting) {
276288
entry.setValue(value);
277289
}
278-
return previousValue;
290+
return oldValue;
279291
}
280292
Assert.state(entries != null, "No entries segment");
281293
entries.add(value);
@@ -342,9 +354,9 @@ public V replace(K key, final V value) {
342354
@Nullable
343355
protected V execute(@Nullable Reference<K, V> reference, @Nullable Entry<K, V> entry) {
344356
if (entry != null) {
345-
V previousValue = entry.getValue();
357+
V oldValue = entry.getValue();
346358
entry.setValue(value);
347-
return previousValue;
359+
return oldValue;
348360
}
349361
return null;
350362
}
@@ -389,7 +401,7 @@ public Set<java.util.Map.Entry<K, V>> entrySet() {
389401
}
390402

391403
@Nullable
392-
private <T> T doTask(Object key, Task<T> task) {
404+
private <T> T doTask(@Nullable Object key, Task<T> task) {
393405
int hash = getHash(key);
394406
return getSegmentForHash(hash).doTask(hash, key, task);
395407
}
@@ -488,7 +500,7 @@ public Reference<K, V> getReference(@Nullable Object key, int hash, Restructure
488500
* @return the result of the operation
489501
*/
490502
@Nullable
491-
public <T> T doTask(final int hash, final Object key, final Task<T> task) {
503+
public <T> T doTask(final int hash, @Nullable final Object key, final Task<T> task) {
492504
boolean resize = task.hasOption(TaskOption.RESIZE);
493505
if (task.hasOption(TaskOption.RESTRUCTURE_BEFORE)) {
494506
restructureIfNecessary(resize);
@@ -504,7 +516,7 @@ public <T> T doTask(final int hash, final Object key, final Task<T> task) {
504516
Entry<K, V> entry = (reference != null ? reference.get() : null);
505517
Entries entries = new Entries() {
506518
@Override
507-
public void add(V value) {
519+
public void add(@Nullable V value) {
508520
@SuppressWarnings("unchecked")
509521
Entry<K, V> newEntry = new Entry<>((K) key, value);
510522
Reference<K, V> newReference = Segment.this.referenceManager.createReference(newEntry, hash, head);
@@ -617,7 +629,7 @@ private Reference<K, V> findInChain(Reference<K, V> reference, @Nullable Object
617629
Entry<K, V> entry = currRef.get();
618630
if (entry != null) {
619631
K entryKey = entry.getKey();
620-
if (entryKey == key || entryKey.equals(key)) {
632+
if (ObjectUtils.nullSafeEquals(entryKey, key)) {
621633
return currRef;
622634
}
623635
}
@@ -688,27 +700,32 @@ protected interface Reference<K, V> {
688700
*/
689701
protected static final class Entry<K, V> implements Map.Entry<K, V> {
690702

703+
@Nullable
691704
private final K key;
692705

706+
@Nullable
693707
private volatile V value;
694708

695-
public Entry(K key, V value) {
709+
public Entry(@Nullable K key, @Nullable V value) {
696710
this.key = key;
697711
this.value = value;
698712
}
699713

700714
@Override
715+
@Nullable
701716
public K getKey() {
702717
return this.key;
703718
}
704719

705720
@Override
721+
@Nullable
706722
public V getValue() {
707723
return this.value;
708724
}
709725

710726
@Override
711-
public V setValue(V value) {
727+
@Nullable
728+
public V setValue(@Nullable V value) {
712729
V previous = this.value;
713730
this.value = value;
714731
return previous;
@@ -800,7 +817,7 @@ private abstract class Entries {
800817
* Add a new entry with the specified value.
801818
* @param value the value to add
802819
*/
803-
public abstract void add(V value);
820+
public abstract void add(@Nullable V value);
804821
}
805822

806823

0 commit comments

Comments
 (0)