@ -35,6 +35,8 @@ import java.util.concurrent.atomic.AtomicInteger;
@@ -35,6 +35,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock ;
import org.springframework.lang.Nullable ;
import org.springframework.util.ConcurrentReferenceHashMap.Reference ;
import org.springframework.util.ConcurrentReferenceHashMap.Restructure ;
/ * *
* A { @link ConcurrentHashMap } that uses { @link ReferenceType # SOFT soft } or
@ -232,27 +234,24 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
@@ -232,27 +234,24 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
@Override
@Nullable
public V get ( @Nullable Object key ) {
Entry < K , V > entry = getEntryIfAvailable ( key ) ;
Reference < K , V > ref = getReference ( key , Restructure . WHEN_NECESSARY ) ;
Entry < K , V > entry = ( ref ! = null ? ref . get ( ) : null ) ;
return ( entry ! = null ? entry . getValue ( ) : null ) ;
}
@Override
@Nullable
public V getOrDefault ( @Nullable Object key , @Nullable V defaultValue ) {
Entry < K , V > entry = getEntryIfAvailable ( key ) ;
Reference < K , V > ref = getReference ( key , Restructure . WHEN_NECESSARY ) ;
Entry < K , V > entry = ( ref ! = null ? ref . get ( ) : null ) ;
return ( entry ! = null ? entry . getValue ( ) : defaultValue ) ;
}
@Override
public boolean containsKey ( @Nullable Object key ) {
Entry < K , V > entry = getEntryIfAvailable ( key ) ;
return ( entry ! = null & & ObjectUtils . nullSafeEquals ( entry . getKey ( ) , key ) ) ;
}
@Nullable
private Entry < K , V > getEntryIfAvailable ( @Nullable Object key ) {
Reference < K , V > ref = getReference ( key , Restructure . WHEN_NECESSARY ) ;
return ( ref ! = null ? ref . get ( ) : null ) ;
Entry < K , V > entry = ( ref ! = null ? ref . get ( ) : null ) ;
return ( entry ! = null & & ObjectUtils . nullSafeEquals ( entry . getKey ( ) , key ) ) ;
}
/ * *
@ -573,65 +572,70 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
@@ -573,65 +572,70 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
* /
protected final void restructureIfNecessary ( boolean allowResize ) {
int currCount = this . count . get ( ) ;
boolean needsResize = ( currCount > 0 & & currCount > = this . resizeThreshold ) ;
boolean needsResize = allowResize & & ( currCount > 0 & & currCount > = this . resizeThreshold ) ;
Reference < K , V > ref = this . referenceManager . pollForPurge ( ) ;
if ( ref ! = null | | ( needsResize & & allowResize ) ) {
lock ( ) ;
try {
int countAfterRestructure = this . count . get ( ) ;
Set < Reference < K , V > > toPurge = Collections . emptySet ( ) ;
if ( ref ! = null ) {
toPurge = new HashSet < > ( ) ;
while ( ref ! = null ) {
toPurge . add ( ref ) ;
ref = this . referenceManager . pollForPurge ( ) ;
}
}
countAfterRestructure - = toPurge . size ( ) ;
// Recalculate taking into account count inside lock and items that
// will be purged
needsResize = ( countAfterRestructure > 0 & & countAfterRestructure > = this . resizeThreshold ) ;
boolean resizing = false ;
int restructureSize = this . references . length ;
if ( allowResize & & needsResize & & restructureSize < MAXIMUM_SEGMENT_SIZE ) {
restructureSize < < = 1 ;
resizing = true ;
if ( ref ! = null | | ( needsResize ) ) {
restructure ( allowResize , ref ) ;
}
}
private void restructure ( boolean allowResize , Reference < K , V > ref ) {
boolean needsResize ;
lock ( ) ;
try {
int countAfterRestructure = this . count . get ( ) ;
Set < Reference < K , V > > toPurge = Collections . emptySet ( ) ;
if ( ref ! = null ) {
toPurge = new HashSet < > ( ) ;
while ( ref ! = null ) {
toPurge . add ( ref ) ;
ref = this . referenceManager . pollForPurge ( ) ;
}
}
countAfterRestructure - = toPurge . size ( ) ;
// Recalculate taking into account count inside lock and items that
// will be purged
needsResize = ( countAfterRestructure > 0 & & countAfterRestructure > = this . resizeThreshold ) ;
boolean resizing = false ;
int restructureSize = this . references . length ;
if ( allowResize & & needsResize & & restructureSize < MAXIMUM_SEGMENT_SIZE ) {
restructureSize < < = 1 ;
resizing = true ;
}
// Either create a new table or reuse the existing one
Reference < K , V > [ ] restructured =
( resizing ? createReferenceArray ( restructureSize ) : this . references ) ;
// Either create a new table or reuse the existing one
Reference < K , V > [ ] restructured =
( resizing ? createReferenceArray ( restructureSize ) : this . references ) ;
// Restructure
for ( int i = 0 ; i < this . references . length ; i + + ) {
ref = this . references [ i ] ;
if ( ! resizing ) {
restructured [ i ] = null ;
}
while ( ref ! = null ) {
if ( ! toPurge . contains ( ref ) ) {
Entry < K , V > entry = ref . get ( ) ;
if ( entry ! = null ) {
int index = getIndex ( ref . getHash ( ) , restructured ) ;
restructured [ index ] = this . referenceManager . createReference (
entry , ref . getHash ( ) , restructured [ index ] ) ;
}
// Restructure
for ( int i = 0 ; i < this . references . length ; i + + ) {
ref = this . references [ i ] ;
if ( ! resizing ) {
restructured [ i ] = null ;
}
while ( ref ! = null ) {
if ( ! toPurge . contains ( ref ) ) {
Entry < K , V > entry = ref . get ( ) ;
if ( entry ! = null ) {
int index = getIndex ( ref . getHash ( ) , restructured ) ;
restructured [ index ] = this . referenceManager . createReference (
entry , ref . getHash ( ) , restructured [ index ] ) ;
}
ref = ref . getNext ( ) ;
}
ref = ref . getNext ( ) ;
}
// Replace volatile members
if ( resizing ) {
this . references = restructured ;
this . resizeThreshold = ( int ) ( this . references . length * getLoadFactor ( ) ) ;
}
this . count . set ( Math . max ( countAfterRestructure , 0 ) ) ;
}
finally {
unlock ( ) ;
// Replace volatile members
if ( resizing ) {
this . references = restructured ;
this . resizeThreshold = ( int ) ( this . references . length * getLoadFactor ( ) ) ;
}
this . count . set ( Math . max ( countAfterRestructure , 0 ) ) ;
}
finally {
unlock ( ) ;
}
}