Skip to content
Permalink
Browse files

8176894: Provide specialized implementation for default methods putIf…

…Absent, computeIfAbsent, computeIfPresent, compute, merge in TreeMap

Co-authored-by: Sergey Kuksenko <sergey.kuksenko@oracle.com>
Reviewed-by: martin, stuefe, rriggs
  • Loading branch information
Tagir F. Valeev and kuksenko committed Apr 2, 2020
1 parent 3790e58 commit 0386b7d0c359e83368a583c88f8b84fd3ca16fc1
@@ -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.
*
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@@ -30,6 +30,7 @@
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiFunction;

import org.testng.annotations.Test;
@@ -53,6 +54,7 @@
new Object[]{new HashMap<>(), true},
new Object[]{new Hashtable<>(), true},
new Object[]{new LinkedHashMap<>(), true},
new Object[]{new TreeMap<>(), true},
// Test default Map methods - no CME
new Object[]{new Defaults.ExtendsAbstractMap<>(), false}
).iterator();

0 comments on commit 0386b7d

Please sign in to comment.