|
|
@@ -1,5 +1,5 @@ |
|
|
/* |
|
|
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. |
|
|
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. |
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
|
* |
|
|
* This code is free software; you can redistribute it and/or modify it |
|
@@ -29,6 +29,7 @@ |
|
|
import java.util.function.BiConsumer; |
|
|
import java.util.function.BiFunction; |
|
|
import java.util.function.Consumer; |
|
|
import java.util.function.Function; |
|
|
|
|
|
/** |
|
|
* A Red-Black tree based {@link NavigableMap} implementation. |
|
@@ -341,8 +342,7 @@ public void putAll(Map<? extends K, ? extends V> map) { |
|
|
// Offload comparator-based version for sake of performance |
|
|
if (comparator != null) |
|
|
return getEntryUsingComparator(key); |
|
|
if (key == null) |
|
|
throw new NullPointerException(); |
|
|
Objects.requireNonNull(key); |
|
|
@SuppressWarnings("unchecked") |
|
|
Comparable<? super K> k = (Comparable<? super K>) key; |
|
|
Entry<K,V> p = root; |
|
@@ -531,15 +531,121 @@ else if (cmp > 0) |
|
|
* does not permit null keys |
|
|
*/ |
|
|
public V put(K key, V value) { |
|
|
return put(key, value, true); |
|
|
} |
|
|
|
|
|
@Override |
|
|
public V putIfAbsent(K key, V value) { |
|
|
return put(key, value, false); |
|
|
} |
|
|
|
|
|
/** |
|
|
* {@inheritDoc} |
|
|
* |
|
|
* <p>This method will, on a best-effort basis, throw a |
|
|
* {@link ConcurrentModificationException} if it is detected that the |
|
|
* mapping function modifies this map during computation. |
|
|
* |
|
|
* @throws ConcurrentModificationException if it is detected that the |
|
|
* mapping function modified this map |
|
|
*/ |
|
|
@Override |
|
|
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { |
|
|
Objects.requireNonNull(mappingFunction); |
|
|
V newValue; |
|
|
Entry<K,V> t = root; |
|
|
if (t == null) { |
|
|
compare(key, key); // type (and possibly null) check |
|
|
newValue = callMappingFunctionWithCheck(key, mappingFunction); |
|
|
if (newValue != null) { |
|
|
addEntryToEmptyMap(key, newValue); |
|
|
return newValue; |
|
|
} else { |
|
|
return null; |
|
|
} |
|
|
} |
|
|
int cmp; |
|
|
Entry<K,V> parent; |
|
|
// split comparator and comparable paths |
|
|
Comparator<? super K> cpr = comparator; |
|
|
if (cpr != null) { |
|
|
do { |
|
|
parent = t; |
|
|
cmp = cpr.compare(key, t.key); |
|
|
if (cmp < 0) |
|
|
t = t.left; |
|
|
else if (cmp > 0) |
|
|
t = t.right; |
|
|
else |
|
|
return t.value; |
|
|
} while (t != null); |
|
|
} else { |
|
|
Objects.requireNonNull(key); |
|
|
@SuppressWarnings("unchecked") |
|
|
Comparable<? super K> k = (Comparable<? super K>) key; |
|
|
do { |
|
|
parent = t; |
|
|
cmp = k.compareTo(t.key); |
|
|
if (cmp < 0) |
|
|
t = t.left; |
|
|
else if (cmp > 0) |
|
|
t = t.right; |
|
|
else |
|
|
return t.value; |
|
|
} while (t != null); |
|
|
} |
|
|
newValue = callMappingFunctionWithCheck(key, mappingFunction); |
|
|
if (newValue != null) { |
|
|
addEntry(key, newValue, parent, cmp < 0); |
|
|
return newValue; |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
root = new Entry<>(key, value, null); |
|
|
size = 1; |
|
|
modCount++; |
|
|
/** |
|
|
* {@inheritDoc} |
|
|
* |
|
|
* <p>This method will, on a best-effort basis, throw a |
|
|
* {@link ConcurrentModificationException} if it is detected that the |
|
|
* remapping function modifies this map during computation. |
|
|
* |
|
|
* @throws ConcurrentModificationException if it is detected that the |
|
|
* remapping function modified this map |
|
|
*/ |
|
|
@Override |
|
|
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { |
|
|
Objects.requireNonNull(remappingFunction); |
|
|
Entry<K,V> oldEntry = getEntry(key); |
|
|
if (oldEntry != null && oldEntry.value != null) { |
|
|
return remapValue(oldEntry, key, remappingFunction); |
|
|
} else { |
|
|
return null; |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* {@inheritDoc} |
|
|
* |
|
|
* <p>This method will, on a best-effort basis, throw a |
|
|
* {@link ConcurrentModificationException} if it is detected that the |
|
|
* remapping function modifies this map during computation. |
|
|
* |
|
|
* @throws ConcurrentModificationException if it is detected that the |
|
|
* remapping function modified this map |
|
|
*/ |
|
|
@Override |
|
|
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { |
|
|
Objects.requireNonNull(remappingFunction); |
|
|
V newValue; |
|
|
Entry<K,V> t = root; |
|
|
if (t == null) { |
|
|
newValue = callRemappingFunctionWithCheck(key, null, remappingFunction); |
|
|
if (newValue != null) { |
|
|
addEntryToEmptyMap(key, newValue); |
|
|
return newValue; |
|
|
} else { |
|
|
return null; |
|
|
} |
|
|
} |
|
|
int cmp; |
|
|
Entry<K,V> parent; |
|
|
// split comparator and comparable paths |
|
@@ -553,14 +659,12 @@ public V put(K key, V value) { |
|
|
else if (cmp > 0) |
|
|
t = t.right; |
|
|
else |
|
|
return t.setValue(value); |
|
|
return remapValue(t, key, remappingFunction); |
|
|
} while (t != null); |
|
|
} |
|
|
else { |
|
|
if (key == null) |
|
|
throw new NullPointerException(); |
|
|
} else { |
|
|
Objects.requireNonNull(key); |
|
|
@SuppressWarnings("unchecked") |
|
|
Comparable<? super K> k = (Comparable<? super K>) key; |
|
|
Comparable<? super K> k = (Comparable<? super K>) key; |
|
|
do { |
|
|
parent = t; |
|
|
cmp = k.compareTo(t.key); |
|
@@ -569,20 +673,188 @@ else if (cmp > 0) |
|
|
else if (cmp > 0) |
|
|
t = t.right; |
|
|
else |
|
|
return t.setValue(value); |
|
|
return remapValue(t, key, remappingFunction); |
|
|
} while (t != null); |
|
|
} |
|
|
newValue = callRemappingFunctionWithCheck(key, null, remappingFunction); |
|
|
if (newValue != null) { |
|
|
addEntry(key, newValue, parent, cmp < 0); |
|
|
return newValue; |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
/** |
|
|
* {@inheritDoc} |
|
|
* |
|
|
* <p>This method will, on a best-effort basis, throw a |
|
|
* {@link ConcurrentModificationException} if it is detected that the |
|
|
* remapping function modifies this map during computation. |
|
|
* |
|
|
* @throws ConcurrentModificationException if it is detected that the |
|
|
* remapping function modified this map |
|
|
*/ |
|
|
@Override |
|
|
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { |
|
|
Objects.requireNonNull(remappingFunction); |
|
|
Objects.requireNonNull(value); |
|
|
Entry<K,V> t = root; |
|
|
if (t == null) { |
|
|
addEntryToEmptyMap(key, value); |
|
|
return value; |
|
|
} |
|
|
int cmp; |
|
|
Entry<K,V> parent; |
|
|
// split comparator and comparable paths |
|
|
Comparator<? super K> cpr = comparator; |
|
|
if (cpr != null) { |
|
|
do { |
|
|
parent = t; |
|
|
cmp = cpr.compare(key, t.key); |
|
|
if (cmp < 0) |
|
|
t = t.left; |
|
|
else if (cmp > 0) |
|
|
t = t.right; |
|
|
else return mergeValue(t, value, remappingFunction); |
|
|
} while (t != null); |
|
|
} else { |
|
|
Objects.requireNonNull(key); |
|
|
@SuppressWarnings("unchecked") |
|
|
Comparable<? super K> k = (Comparable<? super K>) key; |
|
|
do { |
|
|
parent = t; |
|
|
cmp = k.compareTo(t.key); |
|
|
if (cmp < 0) |
|
|
t = t.left; |
|
|
else if (cmp > 0) |
|
|
t = t.right; |
|
|
else return mergeValue(t, value, remappingFunction); |
|
|
} while (t != null); |
|
|
} |
|
|
addEntry(key, value, parent, cmp < 0); |
|
|
return value; |
|
|
} |
|
|
|
|
|
private V callMappingFunctionWithCheck(K key, Function<? super K, ? extends V> mappingFunction) { |
|
|
int mc = modCount; |
|
|
V newValue = mappingFunction.apply(key); |
|
|
if (mc != modCount) { |
|
|
throw new ConcurrentModificationException(); |
|
|
} |
|
|
return newValue; |
|
|
} |
|
|
|
|
|
private V callRemappingFunctionWithCheck(K key, V oldValue, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { |
|
|
int mc = modCount; |
|
|
V newValue = remappingFunction.apply(key, oldValue); |
|
|
if (mc != modCount) { |
|
|
throw new ConcurrentModificationException(); |
|
|
} |
|
|
return newValue; |
|
|
} |
|
|
|
|
|
private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) { |
|
|
Entry<K,V> e = new Entry<>(key, value, parent); |
|
|
if (cmp < 0) |
|
|
if (addToLeft) |
|
|
parent.left = e; |
|
|
else |
|
|
parent.right = e; |
|
|
fixAfterInsertion(e); |
|
|
size++; |
|
|
modCount++; |
|
|
} |
|
|
|
|
|
private void addEntryToEmptyMap(K key, V value) { |
|
|
compare(key, key); // type (and possibly null) check |
|
|
root = new Entry<>(key, value, null); |
|
|
size = 1; |
|
|
modCount++; |
|
|
} |
|
|
|
|
|
private V put(K key, V value, boolean replaceOld) { |
|
|
Entry<K,V> t = root; |
|
|
if (t == null) { |
|
|
addEntryToEmptyMap(key, value); |
|
|
return null; |
|
|
} |
|
|
int cmp; |
|
|
Entry<K,V> parent; |
|
|
// split comparator and comparable paths |
|
|
Comparator<? super K> cpr = comparator; |
|
|
if (cpr != null) { |
|
|
do { |
|
|
parent = t; |
|
|
cmp = cpr.compare(key, t.key); |
|
|
if (cmp < 0) |
|
|
t = t.left; |
|
|
else if (cmp > 0) |
|
|
t = t.right; |
|
|
else { |
|
|
V oldValue = t.value; |
|
|
if (replaceOld || oldValue == null) { |
|
|
t.value = value; |
|
|
} |
|
|
return oldValue; |
|
|
} |
|
|
} while (t != null); |
|
|
} else { |
|
|
Objects.requireNonNull(key); |
|
|
@SuppressWarnings("unchecked") |
|
|
Comparable<? super K> k = (Comparable<? super K>) key; |
|
|
do { |
|
|
parent = t; |
|
|
cmp = k.compareTo(t.key); |
|
|
if (cmp < 0) |
|
|
t = t.left; |
|
|
else if (cmp > 0) |
|
|
t = t.right; |
|
|
else { |
|
|
V oldValue = t.value; |
|
|
if (replaceOld || oldValue == null) { |
|
|
t.value = value; |
|
|
} |
|
|
return oldValue; |
|
|
} |
|
|
} while (t != null); |
|
|
} |
|
|
addEntry(key, value, parent, cmp < 0); |
|
|
return null; |
|
|
} |
|
|
|
|
|
private V remapValue(Entry<K,V> t, K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { |
|
|
V newValue = callRemappingFunctionWithCheck(key, t.value, remappingFunction); |
|
|
if (newValue == null) { |
|
|
deleteEntry(t); |
|
|
return null; |
|
|
} else { |
|
|
// replace old mapping |
|
|
t.value = newValue; |
|
|
return newValue; |
|
|
} |
|
|
} |
|
|
|
|
|
private V mergeValue(Entry<K,V> t, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { |
|
|
V oldValue = t.value; |
|
|
V newValue; |
|
|
if (t.value == null) { |
|
|
newValue = value; |
|
|
} else { |
|
|
int mc = modCount; |
|
|
newValue = remappingFunction.apply(oldValue, value); |
|
|
if (mc != modCount) { |
|
|
throw new ConcurrentModificationException(); |
|
|
} |
|
|
} |
|
|
if (newValue == null) { |
|
|
deleteEntry(t); |
|
|
return null; |
|
|
} else { |
|
|
// replace old mapping |
|
|
t.value = newValue; |
|
|
return newValue; |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Removes the mapping for this key from this TreeMap if present. |
|
|
* |
|
|