mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8176894: Provide specialized implementation for default methods putIfAbsent, computeIfAbsent, computeIfPresent, compute, merge in TreeMap
Co-authored-by: Sergey Kuksenko <sergey.kuksenko@oracle.com> Reviewed-by: martin, stuefe, rriggs
This commit is contained in:
parent
3790e58090
commit
0386b7d0c3
4 changed files with 474 additions and 19 deletions
|
@ -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.io.Serializable;
|
|||
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 class TreeMap<K,V>
|
|||
// 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,14 +531,37 @@ public class TreeMap<K,V>
|
|||
* 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
|
||||
|
||||
root = new Entry<>(key, value, null);
|
||||
size = 1;
|
||||
modCount++;
|
||||
return null;
|
||||
newValue = callMappingFunctionWithCheck(key, mappingFunction);
|
||||
if (newValue != null) {
|
||||
addEntryToEmptyMap(key, newValue);
|
||||
return newValue;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
int cmp;
|
||||
Entry<K,V> parent;
|
||||
|
@ -553,14 +576,12 @@ public class TreeMap<K,V>
|
|||
else if (cmp > 0)
|
||||
t = t.right;
|
||||
else
|
||||
return t.setValue(value);
|
||||
return t.value;
|
||||
} 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 +590,271 @@ public class TreeMap<K,V>
|
|||
else if (cmp > 0)
|
||||
t = t.right;
|
||||
else
|
||||
return t.setValue(value);
|
||||
return t.value;
|
||||
} while (t != null);
|
||||
}
|
||||
newValue = callMappingFunctionWithCheck(key, mappingFunction);
|
||||
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 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
|
||||
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 remapValue(t, key, 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 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.
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue