From 4caeafe27534fa30f416967cb21b4691019700a5 Mon Sep 17 00:00:00 2001 From: Rob Austin Date: Thu, 3 Mar 2016 19:24:21 +0000 Subject: [PATCH] added isEmpty() --- .../openhft/chronicle/map/ChronicleMap.java | 150 ++++++++---------- .../chronicle/map/ChronicleMapBuilder.java | 104 ++++++------ .../chronicle/map/ReplicatedChronicleMap.java | 5 + .../chronicle/map/StatelessChronicleMap.java | 5 + .../chronicle/map/VanillaChronicleMap.java | 7 +- .../chronicle/map/ReplicationCheckingMap.java | 5 + .../jsr166/map/StatelessChronicleMapTest.java | 5 + 7 files changed, 137 insertions(+), 144 deletions(-) diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java index e25dfb498..dabcf472b 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java @@ -30,21 +30,16 @@ import java.util.Map; import java.util.concurrent.ConcurrentMap; /** - * Extension of {@link ConcurrentMap} interface, stores the data off-heap. - *

- *

For information on

see {@link - * ChronicleMapBuilder} documentation. - *

- *

Functionally this interface defines some methods supporting garbage-free off-heap programming: - * {@link #getUsing(Object, Object)}, {@link #acquireUsing(Object, Object)}. - *

- *

Roughly speaking, {@code ChronicleMap} compares keys and values by their binary serialized + * Extension of {@link ConcurrentMap} interface, stores the data off-heap.

For information on + *

see {@link ChronicleMapBuilder} + * documentation.

Functionally this interface defines some methods supporting garbage-free + * off-heap programming: {@link #getUsing(Object, Object)}, {@link #acquireUsing(Object, Object)}. + *

Roughly speaking, {@code ChronicleMap} compares keys and values by their binary serialized * form, that shouldn't necessary be the same equality relation as defined by built-in {@link - * Object#equals(Object)} method, which is prescribed by general {@link Map} contract. - *

- *

Note that {@code ChronicleMap} extends {@link Closeable}, don't forget to {@linkplain #close() - * close} map when it is no longer needed. + * Object#equals(Object)} method, which is prescribed by general {@link Map} contract.

Note + * that {@code ChronicleMap} extends {@link Closeable}, don't forget to {@linkplain #close() close} + * map when it is no longer needed. * * @param the map key type * @param the map value type @@ -60,11 +55,9 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { /** * Returns the value to which the specified key is mapped, or {@code null} if this map contains - * no mapping for the key. - *

- *

If the value class allows reusing, particularly if it is a {@link Byteable} subclass, - * consider {@link #getUsing(Object, Object)} method instead of this to reduce garbage - * creation. + * no mapping for the key.

If the value class allows reusing, particularly if it is a + * {@link Byteable} subclass, consider {@link #getUsing(Object, Object)} method instead of this + * to reduce garbage creation. * * @param key the key whose associated value is to be returned * @return the value to which the specified key is mapped after this method call, or {@code @@ -77,18 +70,15 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { /** * Returns the value to which the specified key is mapped, read to the provided {@code value} * object, if possible, or returns {@code null}, if this map contains no mapping for the key. - *

- *

If the specified key is present in the map, the value data is read to the provided {@code - * value} object via value marshaller's {@link BytesMarshaller#read(Bytes, Object) read(Bytes, - * value)} or value reader's {@link BytesReader#read(Bytes, long, Object) read(Bytes, size, - * value)} method, depending on what deserialization strategy is configured on the builder, - * using which this map was constructed. If the value deserializer is able to reuse the given - * {@code value} object, calling this method instead of {@link #get(Object)} could help to - * reduce garbage creation. - *

- *

The provided {@code value} object is allowed to be {@code null}, in this case {@code - * map.getUsing(key, null)} call is semantically equivalent to simple {@code map.get(key)} - * call. + *

If the specified key is present in the map, the value data is read to the provided + * {@code value} object via value marshaller's {@link BytesMarshaller#read(Bytes, Object) + * read(Bytes, value)} or value reader's {@link BytesReader#read(Bytes, long, Object) + * read(Bytes, size, value)} method, depending on what deserialization strategy is configured on + * the builder, using which this map was constructed. If the value deserializer is able to reuse + * the given {@code value} object, calling this method instead of {@link #get(Object)} could + * help to reduce garbage creation.

The provided {@code value} object is allowed to be + * {@code null}, in this case {@code map.getUsing(key, null)} call is semantically equivalent to + * simple {@code map.get(key)} call. * * @param key the key whose associated value is to be returned * @param usingValue the object to read value data in, if possible @@ -102,20 +92,17 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { /** * The method is similar {@link V getUsing(K key, V usingValue);} but in addition read locks - * the map segment to operations to be performed atomically. ( see the example below ) - *

+ * the map segment to operations to be performed atomically. ( see the example below )

* Returns the ReadContext which holds a map segment lock and provides method to get the value * ( to which the specified key is mapped) atomically ( see example below for an explanation ), * read to the provided {@code value} object, if possible, or returns {@code null}, if this map - * contains no mapping for the key. - *

- *

If the specified key is present in the map, the readContext.value() uses the provided - * {@code usingValue} object via value marshaller's {@link BytesMarshaller#read(Bytes, Object) - * read(Bytes, value)} or value reader's {@link BytesReader#read(Bytes, long, Object) - * read(Bytes, size, value)} method, depending on what deserialization strategy is configured on - * the builder, using which this map was constructed. If the value deserializer is able to reuse - * the given {@code usingValue} object, calling this method instead of {@link #get(Object)} - * could help to reduce garbage creation. + * contains no mapping for the key.

If the specified key is present in the map, the + * readContext.value() uses the provided {@code usingValue} object via value marshaller's {@link + * BytesMarshaller#read(Bytes, Object) read(Bytes, value)} or value reader's {@link + * BytesReader#read(Bytes, long, Object) read(Bytes, size, value)} method, depending on what + * deserialization strategy is configured on the builder, using which this map was constructed. + * If the value deserializer is able to reuse the given {@code usingValue} object, calling this + * method instead of {@link #get(Object)} could help to reduce garbage creation. *

{@code
      * try (ReadContext rc = map.getUsingLocked(key, bond)) {
      *    if (rc.present ()) { // check whether the key was present
@@ -127,16 +114,13 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
      * } // the read lock is released here
      * }
* To ensure that you can read the 'issueDate' and 'symbol' can be read atomically, these values - * must be read while the segment lock is in place. - *

- *

- *

The provided {@code value} object is allowed to be {@code null}, in this case + * must be read while the segment lock is in place.

The provided {@code value} object + * is allowed to be {@code null}, in this case * * @param key the key whose associated value is to be returned * @param usingValue the object to read value data in, if possible - * @return the read context containing the value to which the specified key is mapped - *

- * no mapping for the key + * @return the read context containing the value to which the specified key is mapped

no + * mapping for the key * @see #get(Object) * @see #getUsing(Object, Object) * @see ChronicleMapBuilder#valueMarshaller(BytesMarshaller) @@ -145,32 +129,25 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { ReadContext getUsingLocked(@NotNull K key, @NotNull V usingValue); /** - * Acquire a value for a key, creating if absent. - *

- *

If the specified key is absent in the map, {@linkplain ChronicleMapBuilder#defaultValue(Object) - * default value} is taken or {@linkplain ChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) - * default value provider} is called. Then this object is put to this map for the specified - * key. - *

- *

Then, either if the key was initially absent in the map or already present, the value is + * Acquire a value for a key, creating if absent.

If the specified key is absent in the + * map, {@linkplain ChronicleMapBuilder#defaultValue(Object) default value} is taken or + * {@linkplain ChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) default value + * provider} is called. Then this object is put to this map for the specified key.

Then, + * either if the key was initially absent in the map or already present, the value is * deserialized just as during {@link #getUsing(Object, Object) getUsing(key, usingValue)} call, * passed the same {@code key} and {@code usingValue} as into this method call. This means, as * in {@link #getUsing}, {@code usingValue} could safely be {@code null}, in this case a new - * value instance is created to deserialize the data. - *

- * In code, {@code acquireUsing} is specified as : + * value instance is created to deserialize the data.

In code, {@code acquireUsing} is + * specified as : *

{@code
      * V acquireUsing(K key, V usingValue) {
      *     if (!containsKey(key))
      *         put(key, defaultValue(key));
      *     return getUsing(key, usingValue);
      * }}
- *

- *

- * Where {@code defaultValue(key)} returns either {@linkplain ChronicleMapBuilder#defaultValue(Object) - * default value} or {@link ChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) - * defaultValueProvider.} - *

+ *

Where {@code defaultValue(key)} returns either {@linkplain + * ChronicleMapBuilder#defaultValue(Object) default value} or {@link + * ChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) defaultValueProvider.}

*

If the {@code ChronicleMap} is off-heap updatable, i. e. created via {@link * ChronicleMapBuilder} builder (values are {@link Byteable}), there is one more option of what * to do if the key is absent in the map. By default, value bytes are just zeroed out, no @@ -207,8 +184,8 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { V putMapped(K key, @NotNull UnaryOperator unaryOperator); /** - * A special operation, similar to {@link #replace(Object, Object, Object) - * replace(key, value, value)}, but puts the entry even if it was absent in the map. + * A special operation, similar to {@link #replace(Object, Object, Object) replace(key, value, + * value)}, but puts the entry even if it was absent in the map. */ UpdateResult update(K key, V value); @@ -245,16 +222,12 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { void putAll(File fromFile) throws IOException; /** - * Creates an empty value instance, which can be used with the - * following methods : - *

- * {@link ChronicleMap#getUsing(java.lang.Object, java.lang.Object) } - * {@link ChronicleMap#getUsingLocked(java.lang.Object, java.lang.Object) } - * {@link ChronicleMap#acquireUsing(java.lang.Object, java.lang.Object) } - * {@link ChronicleMap#acquireUsingLocked(java.lang.Object, java.lang.Object) } - *

- * for example like this : - *

+ * Creates an empty value instance, which can be used with the following methods :

{@link + * ChronicleMap#getUsing(java.lang.Object, java.lang.Object) } {@link + * ChronicleMap#getUsingLocked(java.lang.Object, java.lang.Object) } {@link + * ChronicleMap#acquireUsing(java.lang.Object, java.lang.Object) } {@link + * ChronicleMap#acquireUsingLocked(java.lang.Object, java.lang.Object) }

for example like + * this :

*

{@code
      * V value = map.newValueInstance();
      * try (ReadContext rc = map.getUsingLocked(key, value)) {
@@ -271,16 +244,12 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
     V newValueInstance();
 
     /**
-     * Creates an empty value instance, which can be used with the
-     * following methods :
-     * 

- * {@link ChronicleMap#getUsing(java.lang.Object, java.lang.Object) } - * {@link ChronicleMap#getUsingLocked(java.lang.Object, java.lang.Object) } - * {@link ChronicleMap#acquireUsing(java.lang.Object, java.lang.Object) } - * {@link ChronicleMap#acquireUsingLocked(java.lang.Object, java.lang.Object) } - *

- * for example like this : - *

+ * Creates an empty value instance, which can be used with the following methods :

{@link + * ChronicleMap#getUsing(java.lang.Object, java.lang.Object) } {@link + * ChronicleMap#getUsingLocked(java.lang.Object, java.lang.Object) } {@link + * ChronicleMap#acquireUsing(java.lang.Object, java.lang.Object) } {@link + * ChronicleMap#acquireUsingLocked(java.lang.Object, java.lang.Object) }

for example like + * this :

*

{@code
      * K key = map.newKeyInstance();
      * key.setMyStringField("some key");
@@ -311,5 +280,10 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
      * @return the class of {@code }
      */
     Class valueClass();
+
+    /**
+     * the name of the map
+     */
+    String name();
 }
 
diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
index cc8300e7a..498704817 100755
--- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
+++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
@@ -71,26 +71,21 @@ import static net.openhft.lang.model.DataValueGenerator.firstPrimitiveFieldType;
  * 

* ChronicleMap map1 = builder.create(); * ChronicleMap map2 = builder.create();}

- * i. e. created {@code ChronicleMap} instances don't depend on the builder. - *

- *

{@code ChronicleMapBuilder} is mutable, see a note in {@link ChronicleHashBuilder} interface - * documentation. - *

- *

Later in this documentation, "ChronicleMap" means "ChronicleMaps, created by {@code - * ChronicleMapBuilder}", unless specified different, because theoretically someone might provide - * {@code ChronicleMap} implementations with completely different properties. - *

- *

{@code ChronicleMap} ("ChronicleMaps, created by {@code ChronicleMapBuilder}") currently - * doesn't support resizing. That is why you must configure {@linkplain #entries(long) number - * of entries} you are going to insert into the created map at most. See {@link - * #entries(long)} method documentation for more information on this. - *

- *

If you key or value type is not constantly sized and known to {@code ChronicleHashBuilder}, i. - * e. it is not a boxed primitive, data value generated interface, {@link Byteable}, etc. (see the - * complete list TODO insert the link to the complete list), you must provide the {@code - * ChronicleHashBuilder} with some information about you keys or values: if they are - * constantly-sized, call {@link #constantKeySizeBySample(Object)}, otherwise {@link - * ChronicleHashBuilder#averageKeySize(double)} method, accordingly for values. + * i. e. created {@code ChronicleMap} instances don't depend on the builder.

{@code + * ChronicleMapBuilder} is mutable, see a note in {@link ChronicleHashBuilder} interface + * documentation.

Later in this documentation, "ChronicleMap" means "ChronicleMaps, created + * by {@code ChronicleMapBuilder}", unless specified different, because theoretically someone might + * provide {@code ChronicleMap} implementations with completely different properties.

{@code + * ChronicleMap} ("ChronicleMaps, created by {@code ChronicleMapBuilder}") currently doesn't support + * resizing. That is why you must configure {@linkplain #entries(long) number of entries} you + * are going to insert into the created map at most. See {@link #entries(long)} method + * documentation for more information on this.

If you key or value type is not constantly + * sized and known to {@code ChronicleHashBuilder}, i. e. it is not a boxed primitive, data value + * generated interface, {@link Byteable}, etc. (see the complete list TODO insert the link to the + * complete list), you must provide the {@code ChronicleHashBuilder} with some information + * about you keys or values: if they are constantly-sized, call {@link + * #constantKeySizeBySample(Object)}, otherwise {@link ChronicleHashBuilder#averageKeySize(double)} + * method, accordingly for values. * * @param key type of the maps, produced by this builder * @param value type of the maps, produced by this builder @@ -208,6 +203,15 @@ public class ChronicleMapBuilder implements Cloneable, return ((dividend - 1L) / divisor) + 1L; } + public String name() { + return name; + } + + public ChronicleMapBuilder name(String name) { + this.name = name; + return this; + } + private static String pretty(int value) { return value > 0 ? value + "" : "not configured"; } @@ -325,9 +329,8 @@ public class ChronicleMapBuilder implements Cloneable, } /** - * {@inheritDoc} - *

- *

Example: if keys in your map(s) are English words in {@link String} form, average English + * {@inheritDoc}

Example: if keys in your map(s) are English words in {@link String} + * form, average English * word length is 5.1, configure average key size of 6:

{@code
      * ChronicleMap wordFrequencies = ChronicleMapBuilder
      *     .of(String.class, LongValue.class)
@@ -354,8 +357,7 @@ public class ChronicleMapBuilder implements Cloneable,
     }
 
     /**
-     * {@inheritDoc}
-     * 

+ * {@inheritDoc}

*

For example, if your keys are Git commit hashes:

{@code
      * Map gitCommitMessagesByHash =
      *     ChronicleMapBuilder.of(byte[].class, String.class)
@@ -379,17 +381,15 @@ public class ChronicleMapBuilder implements Cloneable,
     /**
      * Configures the average number of bytes, taken by serialized form of values, put into maps,
      * created by this builder. If value size is always the same, call {@link
-     * #constantValueSizeBySample(Object)} method instead of this one.
-     * 

- *

{@code ChronicleHashBuilder} implementation heuristically chooses {@linkplain - * #actualChunkSize(int) the actual chunk size} based on this configuration and the key size, - * that, however, might result to quite high internal fragmentation, i. e. losses because only - * integral number of chunks could be allocated for the entry. If you want to avoid this, you - * should manually configure the actual chunk size in addition to this average value size - * configuration, which is anyway needed. - *

- *

If values are of boxed primitive type or {@link Byteable} subclass, i. e. if value size is - * known statically, it is automatically accounted and shouldn't be specified by user. + * #constantValueSizeBySample(Object)} method instead of this one.

{@code + * ChronicleHashBuilder} implementation heuristically chooses {@linkplain #actualChunkSize(int) + * the actual chunk size} based on this configuration and the key size, that, however, might + * result to quite high internal fragmentation, i. e. losses because only integral number of + * chunks could be allocated for the entry. If you want to avoid this, you should manually + * configure the actual chunk size in addition to this average value size configuration, which + * is anyway needed.

If values are of boxed primitive type or {@link Byteable} subclass, + * i. e. if value size is known statically, it is automatically accounted and shouldn't be + * specified by user. * * @param averageValueSize number of bytes, taken by serialized form of values * @return this builder back @@ -410,13 +410,11 @@ public class ChronicleMapBuilder implements Cloneable, /** * Configures the constant number of bytes, taken by serialized form of values, put into maps, * created by this builder. This is done by providing the {@code sampleValue}, all values should - * take the same number of bytes in serialized form, as this sample object. - *

- *

If values are of boxed primitive type or {@link Byteable} subclass, i. e. if value size is - * known statically, it is automatically accounted and this method shouldn't be called. - *

- *

If value size varies, method {@link #averageValueSize(double)} should be called instead of - * this one. + * take the same number of bytes in serialized form, as this sample object.

If values are + * of boxed primitive type or {@link Byteable} subclass, i. e. if value size is known + * statically, it is automatically accounted and this method shouldn't be called.

If + * value size varies, method {@link #averageValueSize(double)} should be called instead of this + * one. * * @param sampleValue the sample value * @return this builder back @@ -600,18 +598,14 @@ public class ChronicleMapBuilder implements Cloneable, /** * Configures alignment strategy of address in memory of entries and independently of address in - * memory of values within entries in ChronicleMaps, created by this builder. - *

- *

Useful when values of the map are updated intensively, particularly fields with volatile - * access, because it doesn't work well if the value crosses cache lines. Also, on some - * (nowadays rare) architectures any misaligned memory access is more expensive than aligned. - *

- *

If values couldn't reference off-heap memory (i. e. it is not {@link Byteable} or "data - * value generated"), alignment configuration makes no sense and forbidden. - *

- *

Default is {@link Alignment#NO_ALIGNMENT} if values couldn't reference off-heap memory, - * otherwise chosen heuristically (configure explicitly for being sure and to compare - * performance in your case). + * memory of values within entries in ChronicleMaps, created by this builder.

Useful when + * values of the map are updated intensively, particularly fields with volatile access, because + * it doesn't work well if the value crosses cache lines. Also, on some (nowadays rare) + * architectures any misaligned memory access is more expensive than aligned.

If values + * couldn't reference off-heap memory (i. e. it is not {@link Byteable} or "data value + * generated"), alignment configuration makes no sense and forbidden.

Default is {@link + * Alignment#NO_ALIGNMENT} if values couldn't reference off-heap memory, otherwise chosen + * heuristically (configure explicitly for being sure and to compare performance in your case). * * @param alignment the new alignment of the maps constructed by this builder * @return this {@code ChronicleMapOnHeapUpdatableBuilder} back diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index c48b4d5ae..69b554c21 100755 --- a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java @@ -174,6 +174,11 @@ class ReplicatedChronicleMap, @Override public long lastModificationTime(byte remoteIdentifier) { + + if (remoteIdentifier == this.identifier()) { + System.out.println("remoteIdentifier=" + remoteIdentifier); + } + assert remoteIdentifier != this.identifier(); // purposely not volatile as this will impact performance, diff --git a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java index 8f9a6a3ec..661db73ec 100755 --- a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java @@ -198,6 +198,11 @@ class StatelessChronicleMap implements ChronicleMap, Closeable, Clon return vClass; } + @Override + public String name() { + throw new UnsupportedOperationException("todo"); + } + private void checkTimeout(long timeoutTime) { if (timeoutTime < System.currentTimeMillis()) throw new RemoteCallTimeoutException(); diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index 406399363..c24d23cc9 100755 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -93,6 +93,7 @@ class VanillaChronicleMap, private final long lockTimeOutNS; private final int segmentHeaderSize; private final HashSplitting hashSplitting; + private final String name; transient MapEventListener eventListener; transient BytesMapEventListener bytesEventListener; @@ -174,11 +175,15 @@ class VanillaChronicleMap, this.metaDataBytes = builder.metaDataBytes(); this.segmentHeaderSize = builder.segmentHeaderSize(replicated); this.maxChunksPerEntry = builder.maxChunksPerEntry(); - + this.name = builder.name(); hashSplitting = HashSplitting.Splitting.forSegments(actualSegments); initTransients(builder); } + public String name() { + return name; + } + static T newInstance(Class aClass, boolean isKey) { try { return isKey ? DataValueClasses.newInstance(aClass) : diff --git a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java index 54fd1cecb..70493d522 100644 --- a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java +++ b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java @@ -272,6 +272,11 @@ public class ReplicationCheckingMap implements ChronicleMap { return map1.valueClass(); } + @Override + public String name() { + throw new UnsupportedOperationException("todo"); + } + @Override public V acquireUsing(@NotNull K key, V usingValue) { return map1.acquireUsing(key, usingValue); diff --git a/src/test/java/net/openhft/chronicle/map/jsr166/map/StatelessChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/jsr166/map/StatelessChronicleMapTest.java index c38a485ec..51bbb728f 100755 --- a/src/test/java/net/openhft/chronicle/map/jsr166/map/StatelessChronicleMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/jsr166/map/StatelessChronicleMapTest.java @@ -224,6 +224,11 @@ public class StatelessChronicleMapTest extends JSR166TestCase { return d.valueClass(); } + @Override + public String name() { + throw new UnsupportedOperationException("todo"); + } + @Override public V putIfAbsent(K key, V value) { return d.putIfAbsent(key, value);