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
src/java.base/share/classes/java/util
test
jdk/java/util/Map
micro/org/openjdk/bench/java/util
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.BiConsumer;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Red-Black tree based {@link NavigableMap} implementation.
|
* 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
|
// Offload comparator-based version for sake of performance
|
||||||
if (comparator != null)
|
if (comparator != null)
|
||||||
return getEntryUsingComparator(key);
|
return getEntryUsingComparator(key);
|
||||||
if (key == null)
|
Objects.requireNonNull(key);
|
||||||
throw new NullPointerException();
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Comparable<? super K> k = (Comparable<? super K>) key;
|
Comparable<? super K> k = (Comparable<? super K>) key;
|
||||||
Entry<K,V> p = root;
|
Entry<K,V> p = root;
|
||||||
@ -531,14 +531,37 @@ public class TreeMap<K,V>
|
|||||||
* does not permit null keys
|
* does not permit null keys
|
||||||
*/
|
*/
|
||||||
public V put(K key, V value) {
|
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;
|
Entry<K,V> t = root;
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
compare(key, key); // type (and possibly null) check
|
newValue = callMappingFunctionWithCheck(key, mappingFunction);
|
||||||
|
if (newValue != null) {
|
||||||
root = new Entry<>(key, value, null);
|
addEntryToEmptyMap(key, newValue);
|
||||||
size = 1;
|
return newValue;
|
||||||
modCount++;
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int cmp;
|
int cmp;
|
||||||
Entry<K,V> parent;
|
Entry<K,V> parent;
|
||||||
@ -553,14 +576,12 @@ public class TreeMap<K,V>
|
|||||||
else if (cmp > 0)
|
else if (cmp > 0)
|
||||||
t = t.right;
|
t = t.right;
|
||||||
else
|
else
|
||||||
return t.setValue(value);
|
return t.value;
|
||||||
} while (t != null);
|
} while (t != null);
|
||||||
}
|
} else {
|
||||||
else {
|
Objects.requireNonNull(key);
|
||||||
if (key == null)
|
|
||||||
throw new NullPointerException();
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Comparable<? super K> k = (Comparable<? super K>) key;
|
Comparable<? super K> k = (Comparable<? super K>) key;
|
||||||
do {
|
do {
|
||||||
parent = t;
|
parent = t;
|
||||||
cmp = k.compareTo(t.key);
|
cmp = k.compareTo(t.key);
|
||||||
@ -569,20 +590,271 @@ public class TreeMap<K,V>
|
|||||||
else if (cmp > 0)
|
else if (cmp > 0)
|
||||||
t = t.right;
|
t = t.right;
|
||||||
else
|
else
|
||||||
return t.setValue(value);
|
return t.value;
|
||||||
} while (t != null);
|
} 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);
|
Entry<K,V> e = new Entry<>(key, value, parent);
|
||||||
if (cmp < 0)
|
if (addToLeft)
|
||||||
parent.left = e;
|
parent.left = e;
|
||||||
else
|
else
|
||||||
parent.right = e;
|
parent.right = e;
|
||||||
fixAfterInsertion(e);
|
fixAfterInsertion(e);
|
||||||
size++;
|
size++;
|
||||||
modCount++;
|
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;
|
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.
|
* 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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,6 +30,7 @@ import java.util.Hashtable;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
@ -53,6 +54,7 @@ public class FunctionalCMEs {
|
|||||||
new Object[]{new HashMap<>(), true},
|
new Object[]{new HashMap<>(), true},
|
||||||
new Object[]{new Hashtable<>(), true},
|
new Object[]{new Hashtable<>(), true},
|
||||||
new Object[]{new LinkedHashMap<>(), true},
|
new Object[]{new LinkedHashMap<>(), true},
|
||||||
|
new Object[]{new TreeMap<>(), true},
|
||||||
// Test default Map methods - no CME
|
// Test default Map methods - no CME
|
||||||
new Object[]{new Defaults.ExtendsAbstractMap<>(), false}
|
new Object[]{new Defaults.ExtendsAbstractMap<>(), false}
|
||||||
).iterator();
|
).iterator();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,11 +27,19 @@
|
|||||||
* @run testng/othervm -Dtest.map.collisions.shortrun=true InPlaceOpsCollisions
|
* @run testng/othervm -Dtest.map.collisions.shortrun=true InPlaceOpsCollisions
|
||||||
* @summary Ensure overrides of in-place operations in Maps behave well with lots of collisions.
|
* @summary Ensure overrides of in-place operations in Maps behave well with lots of collisions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.testng.annotations.DataProvider;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
import static org.testng.Assert.assertTrue;
|
import static org.testng.Assert.assertTrue;
|
||||||
import static org.testng.Assert.assertFalse;
|
import static org.testng.Assert.assertFalse;
|
||||||
@ -71,6 +79,19 @@ public class InPlaceOpsCollisions extends MapWithCollisionsProviders {
|
|||||||
String.format("map expected size m%d != k%d", map.size(), keys.length));
|
String.format("map expected size m%d != k%d", map.size(), keys.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(dataProvider = "nullValueFriendlyMaps")
|
||||||
|
void testPutIfAbsentOverwriteNull(String desc, Supplier<Map<Object, Object>> ms) {
|
||||||
|
Map<Object, Object> map = ms.get();
|
||||||
|
map.put("key", null);
|
||||||
|
assertEquals(map.size(), 1, desc + ": size != 1");
|
||||||
|
assertTrue(map.containsKey("key"), desc + ": does not have key");
|
||||||
|
assertNull(map.get("key"), desc + ": value is not null");
|
||||||
|
map.putIfAbsent("key", "value"); // must rewrite
|
||||||
|
assertEquals(map.size(), 1, desc + ": size != 1");
|
||||||
|
assertTrue(map.containsKey("key"), desc + ": does not have key");
|
||||||
|
assertEquals(map.get("key"), "value", desc + ": value is not 'value'");
|
||||||
|
}
|
||||||
|
|
||||||
@Test(dataProvider = "mapsWithObjectsAndStrings")
|
@Test(dataProvider = "mapsWithObjectsAndStrings")
|
||||||
void testRemoveMapping(String desc, Supplier<Map<Object, Object>> ms, Object val) {
|
void testRemoveMapping(String desc, Supplier<Map<Object, Object>> ms, Object val) {
|
||||||
Map<Object, Object> map = ms.get();
|
Map<Object, Object> map = ms.get();
|
||||||
@ -496,4 +517,13 @@ public class InPlaceOpsCollisions extends MapWithCollisionsProviders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DataProvider
|
||||||
|
public Iterator<Object[]> nullValueFriendlyMaps() {
|
||||||
|
return Arrays.asList(
|
||||||
|
new Object[]{"HashMap", (Supplier<Map<?, ?>>) HashMap::new},
|
||||||
|
new Object[]{"LinkedHashMap", (Supplier<Map<?, ?>>) LinkedHashMap::new},
|
||||||
|
new Object[]{"TreeMap", (Supplier<Map<?, ?>>) TreeMap::new},
|
||||||
|
new Object[]{"TreeMap(cmp)", (Supplier<Map<?, ?>>) () -> new TreeMap<>(Comparator.reverseOrder())}
|
||||||
|
).iterator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
151
test/micro/org/openjdk/bench/java/util/TreeMapUpdate.java
Normal file
151
test/micro/org/openjdk/bench/java/util/TreeMapUpdate.java
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package org.openjdk.bench.java.util;
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
|
import org.openjdk.jmh.annotations.Fork;
|
||||||
|
import org.openjdk.jmh.annotations.Measurement;
|
||||||
|
import org.openjdk.jmh.annotations.Mode;
|
||||||
|
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||||
|
import org.openjdk.jmh.annotations.Param;
|
||||||
|
import org.openjdk.jmh.annotations.Scope;
|
||||||
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
|
import org.openjdk.jmh.annotations.State;
|
||||||
|
import org.openjdk.jmh.annotations.Warmup;
|
||||||
|
import org.openjdk.jmh.infra.Blackhole;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
|
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||||
|
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||||
|
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||||
|
@Fork(3)
|
||||||
|
@State(Scope.Thread)
|
||||||
|
public class TreeMapUpdate {
|
||||||
|
@Param({"10", "1000", "100000"})
|
||||||
|
public int size;
|
||||||
|
|
||||||
|
@Param({"true", "false"})
|
||||||
|
public boolean comparator;
|
||||||
|
|
||||||
|
@Param({"true", "false"})
|
||||||
|
public boolean preFill;
|
||||||
|
|
||||||
|
@Param({"0"})
|
||||||
|
public long seed;
|
||||||
|
|
||||||
|
private Supplier<TreeMap<Integer, Integer>> supplier;
|
||||||
|
|
||||||
|
private Integer[] keys;
|
||||||
|
|
||||||
|
@Setup
|
||||||
|
public void setUp() {
|
||||||
|
supplier = comparator ? () -> new TreeMap<>(Comparator.reverseOrder()) : TreeMap::new;
|
||||||
|
keys = IntStream.range(0, size).boxed().toArray(Integer[]::new);
|
||||||
|
Random rnd = seed == 0 ? new Random() : new Random(seed);
|
||||||
|
Collections.shuffle(Arrays.asList(keys, rnd));
|
||||||
|
if (preFill) {
|
||||||
|
TreeMap<Integer, Integer> template = Arrays.stream(keys)
|
||||||
|
.collect(Collectors.toMap(Function.identity(), Function.identity(), (a, b) -> a, supplier));
|
||||||
|
supplier = () -> new TreeMap<>(template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public Map<Integer, Integer> baseline() {
|
||||||
|
// Just create map (empty or pre-filled)
|
||||||
|
return supplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public Map<Integer, Integer> put(Blackhole bh) {
|
||||||
|
Map<Integer, Integer> map = supplier.get();
|
||||||
|
Integer[] keys = this.keys;
|
||||||
|
for (Integer key : keys) {
|
||||||
|
bh.consume(map.put(key, key));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public Map<Integer, Integer> putIfAbsent(Blackhole bh) {
|
||||||
|
Map<Integer, Integer> map = supplier.get();
|
||||||
|
Integer[] keys = this.keys;
|
||||||
|
for (Integer key : keys) {
|
||||||
|
bh.consume(map.putIfAbsent(key, key));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public Map<Integer, Integer> computeIfAbsent(Blackhole bh) {
|
||||||
|
Map<Integer, Integer> map = supplier.get();
|
||||||
|
Integer[] keys = this.keys;
|
||||||
|
for (Integer key : keys) {
|
||||||
|
bh.consume(map.computeIfAbsent(key, k -> k));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public Map<Integer, Integer> compute(Blackhole bh) {
|
||||||
|
Map<Integer, Integer> map = supplier.get();
|
||||||
|
Integer[] keys = this.keys;
|
||||||
|
for (Integer key : keys) {
|
||||||
|
bh.consume(map.compute(key, (k, old) -> k));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public Map<Integer, Integer> computeIfPresent(Blackhole bh) {
|
||||||
|
Map<Integer, Integer> map = supplier.get();
|
||||||
|
Integer[] keys = this.keys;
|
||||||
|
for (Integer key : keys) {
|
||||||
|
bh.consume(map.computeIfPresent(key, (k, old) -> k));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public Map<Integer, Integer> merge(Blackhole bh) {
|
||||||
|
Map<Integer, Integer> map = supplier.get();
|
||||||
|
Integer[] keys = this.keys;
|
||||||
|
for (Integer key : keys) {
|
||||||
|
bh.consume(map.merge(key, key, (k1, k2) -> k1));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user