diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
index 616b53ad5..0b80b6b9b 100755
--- a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
+++ b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
@@ -33,19 +33,19 @@ import java.util.concurrent.Future;
/**
* Extension of {@link ConcurrentMap} interface, stores the data off-heap.
*
- *
For information on
- how to construct a {@code ChronicleMap}
- {@code ChronicleMap}
- * flavors and properties
- available configurations
see {@link AbstractChronicleMapBuilder}
- * documentation.
+ * For information on
- how to construct a {@code ChronicleMap}
- {@code
+ * ChronicleMap} flavors and properties
- available configurations
see {@link
+ * AbstractChronicleMapBuilder} documentation.
*
- * Functionally this interface defines some methods supporting garbage-free off-heap programming: {@link
- * #getUsing(Object, Object)}, {@link #acquireUsing(Object, Object)}.
+ *
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.
+ *
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.
+ *
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,38 +60,41 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
long longSize();
/**
- * Returns the value to which the specified key is mapped, or {@code null} if this map contains no mapping
- * for the key.
+ * 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.
+ *
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 null} if no
- * value is mapped
+ * @return the value to which the specified key is mapped after this method call, or {@code
+ * null} if no value is mapped
* @see #getUsing(Object, Object)
*/
@Override
V get(Object key);
/**
- * 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.
+ * 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.
+ *
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.
+ * 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
- * @return the value to which the specified key is mapped, or {@code null} if this map contains no mapping
- * for the key
+ * @return the value to which the specified key is mapped, or {@code null} if this map contains
+ * no mapping for the key
* @see #get(Object)
* @see #acquireUsing(Object, Object)
* @see OnHeapUpdatableChronicleMapBuilder#valueMarshaller(BytesMarshaller)
@@ -99,31 +102,33 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
V getUsing(K key, V usingValue);
/**
- * 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 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 )
*
- * 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.
+ * 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.
+ *
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
* long issueDate = bond.getIssueDate();
* String symbol = bond.getSymbol();
- * // add your logic here ( the lock will ensure this bond can not be changed by another thread )
+ * // add your logic here ( the lock will ensure this bond can not be changed by another thread
+ * )
* }
* } // 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.
+ * 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
@@ -145,13 +150,14 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
*
* If the specified key is absent in the map, {@linkplain AbstractChronicleMapBuilder#defaultValue(Object)
* default value} is taken or {@linkplain AbstractChronicleMapBuilder#defaultValueProvider(DefaultValueProvider)
- * default value provider} is called. Then this object is put to this map for the specified key.
+ * 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.
+ * 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 :
*
{@code
@@ -166,11 +172,11 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
* default value} or {@link AbstractChronicleMapBuilder#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, see {@link ChronicleMapBuilder#prepareValueBytesOnAcquire(PrepareValueBytes)}. By default,
- * value bytes are just zeroed out, no default value, either provided for key or constant, is put for the
- * absent key.
+ *
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, see {@link ChronicleMapBuilder#prepareValueBytesOnAcquire(PrepareValueBytes)}.
+ * By default, value bytes are just zeroed out, no default value, either provided for key or
+ * constant, is put for the absent key.
*
* @param key the key whose associated value is to be returned
* @param usingValue the object to read value data in, if present. Can not be null
@@ -194,8 +200,8 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
R mapForKey(K key, @NotNull Function super V, R> function);
/**
- * Apply a mutator to the value for a key and return a result. A write lock is assumed. If there is no
- * entry for this key it will be created and the value empty.
+ * Apply a mutator to the value for a key and return a result. A write lock is assumed.
If
+ * there is no entry for this key it will be created and the value empty.
*
* @param key to apply the mapping to
* @param mutator to alter the value and calculate a result
@@ -205,13 +211,16 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
R updateForKey(K key, @NotNull Mutator super V, R> mutator);
/**
- * Returns a {@link java.util.concurrent.Future} containing the value to which the specified key is
- * mapped, or {@code null} if this map contains no mapping for the key. This method behaves the same as
- * {@link java.util.Map#get(java.lang.Object)} yet wraps result in a future a {@link
+ * Returns a {@link java.util.concurrent.Future} containing the value to which the specified key
+ * is mapped, or {@code null} if this map contains no mapping for the key. This method behaves
+ * the same as {@link java.util.Map#get(java.lang.Object)} yet wraps result in a future a {@link
* java.util.concurrent.Future}.
*
- * @return a future containing the value to which the specified key is mapped after this method call, or
- * {@code null} if no value is mapped
+ * Calling this method may or may not block until the operating is complete depending on the
+ * implementation.
+ *
+ * @return a future containing the value to which the specified key is mapped after this method
+ * call, or {@code null} if no value is mapped
* @see java.util.Map#get(java.lang.Object)
* @see java.util.concurrent.Future
*/
@@ -219,18 +228,46 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
/**
- * Associates the specified value with the specified key in this map (optional operation). If the map
- * previously contained a mapping for the key, the old value is replaced by the specified value. (A map
- * m is said to contain a mapping for a key k if and only if {@link #containsKey(Object)
- * m.containsKey(k)} would return true.)
+ * Associates the specified value with the specified key in this map (optional operation). If
+ * the map previously contained a mapping for the key, the old value is replaced by the
+ * specified value. (A map m is said to contain a mapping for a key k if and
+ * only if {@link #containsKey(Object) m.containsKey(k)} would return true.)
*
- * @return a future containing the previous value associated with key, or null if there
- * was no mapping for key. (A null return can also indicate that the map previously
- * associated null with key, if the implementation supports null values.)
- * @see java.util.Map#get(java.lang.Object)
+ * Calling this method may or may not block until the operating is complete depending on the
+ * implementation.
+ *
+ * @return a future containing the previous value associated with key, or null
+ * if there was no mapping for key. (A null return can also indicate that the
+ * map previously associated null with key, if the implementation supports
+ * null values.)
+ * @see java.util.Map#put(java.lang.Object, java.lang.Object)
* @see java.util.concurrent.Future
*/
- Future putLater(@NotNull K key,@NotNull V value);
+ Future putLater(@NotNull K key, @NotNull V value);
+
+
+ /**
+ * Removes the mapping for a key from this map if it is present (optional operation). More
+ * formally, if this map contains a mapping from key k to value v such that
+ * (key==null ? k==null : key.equals(k)), that mapping is removed. (The map can
+ * contain at most one such mapping.)
+ *
+ * Returns a future containing, either the value to which this map previously associated the
+ * key, or null if the map contained no mapping for the key.
+ *
+ * This method behaves the same as {@link java.util.Map#remove(java.lang.Object)} yet wraps
+ * result in a future a {@link java.util.concurrent.Future}.
+ *
+ * Calling this method may or may not block until the operating is complete depending on the
+ * implementation.
+ *
+ * @param key key whose mapping is to be removed from the map
+ * @return a future containing either, the previous value associated with key, or
+ * null if there was no mapping for key.
+ * @see java.util.Map#remove(java.lang.Object)
+ * @see java.util.concurrent.Future
+ */
+ Future removeLater(@NotNull K key);
}
diff --git a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java
index 757c43f39..759359e61 100644
--- a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java
+++ b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java
@@ -155,6 +155,18 @@ class StatelessChronicleMap implements ChronicleMap, Closeable, Clon
});
}
+ @Override
+ public Future removeLater(@NotNull final K key) {
+ return lazyExecutorService().submit(new Callable() {
+ @Override
+ public V call() throws Exception {
+
+ V oldValue = StatelessChronicleMap.this.remove(key);
+ return oldValue;
+ }
+ });
+ }
+
private ExecutorService lazyExecutorService() {
@@ -485,7 +497,7 @@ class StatelessChronicleMap implements ChronicleMap, Closeable, Clon
if (key == null)
throw new NullPointerException("key can not be null");
- final long sizeLocation = (removeReturnsNull) ? writeEvent(REMOVE_WITHOUT_ACC) : writeEventAnSkip(REMOVE);
+ final long sizeLocation = removeReturnsNull ? writeEvent(REMOVE_WITHOUT_ACC) : writeEventAnSkip(REMOVE);
writeKey((K) key);
diff --git a/src/main/java/net/openhft/chronicle/map/TcpReplicator.java b/src/main/java/net/openhft/chronicle/map/TcpReplicator.java
index bef98ca3e..135972782 100644
--- a/src/main/java/net/openhft/chronicle/map/TcpReplicator.java
+++ b/src/main/java/net/openhft/chronicle/map/TcpReplicator.java
@@ -1641,6 +1641,7 @@ class StatelessServerConnector {
final K key = readKey(reader, local);
try {
+ LOG.info("removing entry key="+key);
writeValue(map.remove(key), writer, local);
} catch (Throwable e) {
return sendException(writer, sizeLocation, e);
diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java
index aff8d7053..50a71642b 100755
--- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java
+++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java
@@ -58,7 +58,8 @@ class VanillaChronicleMap,
implements ChronicleMap, Serializable {
/**
- * Because DirectBitSet implementations couldn't find more than 64 continuous clear or set bits.
+ * Because DirectBitSet implementations couldn't find more than 64 continuous clear or set
+ * bits.
*/
static final int MAX_ENTRY_OVERSIZE_FACTOR = 64;
static final ThreadLocal tmpBytes = new ThreadLocal<>();
@@ -448,6 +449,38 @@ class VanillaChronicleMap,
};
}
+ @Override
+ public Future removeLater(@NotNull K key) {
+ final V v = remove(key);
+ return new Future() {
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public V get() throws InterruptedException, ExecutionException {
+ return v;
+ }
+
+ @Override
+ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return v;
+ }
+ };
+ }
+
@NotNull
@Override
public WriteContext acquireUsingLocked(@NotNull K key, @NotNull V usingValue) {
@@ -569,9 +602,9 @@ class VanillaChronicleMap,
/**
- * removes ( if there exists ) an entry from the map, if the {@param key} and {@param expectedValue} match
- * that of a maps.entry. If the {@param expectedValue} equals null then ( if there exists ) an entry whose
- * key equals {@param key} this is removed.
+ * removes ( if there exists ) an entry from the map, if the {@param key} and {@param
+ * expectedValue} match that of a maps.entry. If the {@param expectedValue} equals null then (
+ * if there exists ) an entry whose key equals {@param key} this is removed.
*
* @param k the key of the entry to remove
* @param expectedValue null if not required
@@ -631,8 +664,8 @@ class VanillaChronicleMap,
* replace the value in a map, only if the existing entry equals {@param existingValue}
*
* @param key the key into the map
- * @param existingValue the expected existing value in the map ( could be null when we don't wish to do
- * this check )
+ * @param existingValue the expected existing value in the map ( could be null when we don't
+ * wish to do this check )
* @param newValue the new value you wish to store in the map
* @return the value that was replaced
*/
@@ -1174,13 +1207,13 @@ class VanillaChronicleMap,
}
/**
- * Removes a key (or key-value pair) from the Segment. The entry will only be removed if
- * {@code expectedValue} equals to {@code null} or the value previously corresponding to the specified
- * key.
+ * Removes a key (or key-value pair) from the Segment.
The entry will only be
+ * removed if {@code expectedValue} equals to {@code null} or the value previously
+ * corresponding to the specified key.
*
* @param hash2 a hash code related to the {@code keyBytes}
- * @return the value of the entry that was removed if the entry corresponding to the {@code keyBytes}
- * exists and {@link #removeReturnsNull} is {@code false}, {@code null} otherwise
+ * @return the value of the entry that was removed if the entry corresponding to the {@code
+ * keyBytes} exists and {@link #removeReturnsNull} is {@code false}, {@code null} otherwise
*/
V remove(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key,
V expectedValue, long hash2) {
@@ -1240,9 +1273,9 @@ class VanillaChronicleMap,
}
/**
- * Replaces the specified value for the key with the given value. {@code newValue} is set only if the
- * existing value corresponding to the specified key is equal to {@code expectedValue} or {@code
- * expectedValue == null}.
+ * Replaces the specified value for the key with the given value. {@code newValue} is set
+ * only if the existing value corresponding to the specified key is equal to {@code
+ * expectedValue} or {@code expectedValue == null}.
*
* @param hash2 a hash code related to the {@code keyBytes}
* @return the replaced value or {@code null} if the value was not replaced
@@ -1319,17 +1352,17 @@ class VanillaChronicleMap,
}
/**
- * Replaces value in existing entry. May cause entry relocation, because there may be not enough space
- * for new value in location already allocated for this entry.
+ * Replaces value in existing entry. May cause entry relocation, because there may be not
+ * enough space for new value in location already allocated for this entry.
*
* @param pos index of the first block occupied by the entry
- * @param offset relative offset of the entry in Segment bytes (before, i. e. including
- * metaData)
+ * @param offset relative offset of the entry in Segment bytes (before, i. e.
+ * including metaData)
* @param entry relative pointer in Segment bytes
* @param valueSizePos relative position of value size in entry
* @param entryEndAddr absolute address of the entry end
- * @return relative offset of the entry in Segment bytes after putting value (that may cause entry
- * relocation)
+ * @return relative offset of the entry in Segment bytes after putting value (that may cause
+ * entry relocation)
*/
long putValue(long pos, long offset, NativeBytes entry, long valueSizePos,
long entryEndAddr, ThreadLocalCopies copies, V value, Bytes valueBytes,
diff --git a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java
index 3e267c142..d4176cb45 100644
--- a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java
+++ b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java
@@ -265,6 +265,11 @@ public class ReplicationCheckingMap implements ChronicleMap {
throw new UnsupportedOperationException();
}
+ @Override
+ public Future removeLater(@NotNull K key) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public V acquireUsing(@NotNull K key, V usingValue) {
return map1.acquireUsing(key, usingValue);
diff --git a/src/test/java/net/openhft/chronicle/map/StatelessClientTest.java b/src/test/java/net/openhft/chronicle/map/StatelessClientTest.java
index 249fb3c3b..08928a07d 100644
--- a/src/test/java/net/openhft/chronicle/map/StatelessClientTest.java
+++ b/src/test/java/net/openhft/chronicle/map/StatelessClientTest.java
@@ -448,6 +448,27 @@ public class StatelessClientTest {
}
+ @Test
+ public void testRemoveLater() throws IOException,
+ InterruptedException, ExecutionException {
+
+ try (ChronicleMap serverMap = ChronicleMapBuilder.of(Integer.class, CharSequence.class)
+ .replication((byte) 2, TcpTransportAndNetworkConfig.of(8056)).create()) {
+
+ try (ChronicleMap statelessMap = ChronicleMapBuilder.of(Integer
+ .class, CharSequence.class)
+ .statelessClient(new InetSocketAddress("localhost", 8056)).create()) {
+ statelessMap.put(1, "some value");
+ statelessMap.removeLater(1).get();
+ Assert.assertEquals(null, statelessMap.get(1));
+ Assert.assertEquals(0, statelessMap.size());
+
+ }
+ }
+
+ }
+
+
@Test
public void testEquals() throws IOException, InterruptedException {
diff --git a/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java
index 5c181ec4b..c67cf2d06 100755
--- a/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java
+++ b/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java
@@ -187,6 +187,11 @@ public class StatelessChronicleMapTest extends JSR166TestCase {
return d.putLater(key, value);
}
+ @Override
+ public Future removeLater(@NotNull K key) {
+ return d.removeLater(key);
+ }
+
@Override
public V putIfAbsent(K key, V value) {
return d.putIfAbsent(key, value);