diff --git a/src/main/java/net/openhft/chronicle/ChronicleHashBuilder.java b/src/main/java/net/openhft/chronicle/ChronicleHashBuilder.java new file mode 100644 index 000000000..53345e6b9 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/ChronicleHashBuilder.java @@ -0,0 +1,222 @@ +package net.openhft.chronicle; + +import net.openhft.chronicle.map.*; +import net.openhft.chronicle.set.ChronicleSetBuilder; +import org.jetbrains.annotations.NotNull; + +import net.openhft.lang.io.serialization.*; +import net.openhft.lang.model.Byteable; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * This interface defines the meaning of configurations, common to {@link ChronicleMapBuilder} + * and {@link ChronicleSetBuilder}, i. e. Chronicle hash container configurations. + * + * @param the type of keys in hash containers, created by this builder + * @param the container type, created by this builder, i. e. {@link ChronicleMap} + * or {@link ChronicleSet} + * @param the concrete builder type, i. e. {@link ChronicleMapBuilder} + * or {@link ChronicleSetBuilder} + */ +public interface ChronicleHashBuilder> + extends Cloneable { + + B clone(); + + /** + * Set minimum number of segments. See concurrencyLevel in {@link ConcurrentHashMap}. + * + * @param minSegments the minimum number of segments in containers, constructed by this builder + * @return this builder object back + */ + B minSegments(int minSegments); + + /** + * Configures the optimal number of bytes, taken by serialized form of keys, put into hash + * containers, created by this builder. If key size is always the same, call + * {@link #constantKeySizeBySample(Object)} method instead of this one. + * + *

If key size varies moderately, specify the size higher than average, but lower than + * the maximum possible, to minimize average memory overuse. If key size varies in a wide range, + * it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it + * directly. + * + * @param keySize number of bytes, taken by serialized form of keys + * @return this builder back + * @see #constantKeySizeBySample(Object) + * @see #entrySize(int) + */ + B keySize(int keySize); + + /** + * Configures the constant number of bytes, taken by serialized form of keys, put into hash + * containers, created by this builder. This is done by providing the {@code sampleKey}, + * all keys should take the same number of bytes in serialized form, as this sample object. + * + *

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

If key size varies, method {@link #keySize(int)} or {@link #entrySize(int)} should be + * called instead of this one. + * + * @param sampleKey the sample key + * @return this builder back + * @see #keySize(int) + */ + B constantKeySizeBySample(K sampleKey); + + /** + * Configures the size in bytes of allocation unit of hash container instances, created by + * this builder. + * + *

{@link ChronicleMap} and {@link ChronicleSet} store their data off-heap, so it is required + * to serialize key (and values, in {@code ChronicleMap} case) (unless they are direct + * {@link Byteable} instances). Serialized key bytes (+ serialized value bytes, + * in {@code ChronicleMap} case) + some metadata bytes comprise "entry space", which + * {@code ChronicleMap} or {@code ChronicleSet} should allocate. So entry size + * is a minimum allocation portion in the hash containers, created by this builder. + * E. g. if entry size is 100, the created container could only allocate 100, 200, 300... bytes + * for an entry. If say 150 bytes of entry space are required by the entry, 200 bytes + * will be allocated, 150 used and 50 wasted. To minimize memory overuse and improve speed, you + * should pay decent attention to this configuration. + * + *

There are three major patterns of this configuration usage: + *

    + *
  1. Key (and value, in {@code ChronicleMap} case) sizes are constant. Configure them + * via {@link #constantKeySizeBySample(Object)} and + * {@link ChronicleMapBuilder#constantValueSizeBySample(Object)} methods, and you will + * experience no memory waste at all.
  2. + *
  3. Key (and/or value size, in {@code ChronicleMap} case) varies moderately. Specify them + * using corresponding methods, or specify entry size directly by calling this method, + * by sizes somewhere between average and maximum possible. The idea is to have most + * (90% or more) entries to fit a single "entry size" with moderate memory waste + * (10-20% on average), rest 10% or less of entries should take 2 "entry sizes", thus with + * ~50% memory overuse.
  4. + *
  5. Key (and/or value size, in {@code ChronicleMap} case) varies in a wide range. + * Then it's best to use entry size configuration in chunk mode. Specify entry size + * so that most entries should take from 5 to several dozens of "chunks". With this + * approach, average memory waste should be very low. + * + *

    However, remember that + *

      + *
    • Operations with entries that span several "entry sizes" are a bit slower, than + * entries which take a single "entry size". That is why "chunk" approach + * is not recommended, when key (and/or value size) varies moderately.
    • + *
    • The maximum number of chunks could be taken by an entry is 64. + * {@link IllegalArgumentException} is thrown on attempt to insert too large entry, + * compared to the configured or computed entry size.
    • + *
    • The number of "entries" {@linkplain #entries(long) in the whole map} and + * {@linkplain #actualEntriesPerSegment(int) per segment} is actually the number + * "chunks".
    • + *
    + * + *

    Example: if values in your {@code ChronicleMap} are adjacency lists of some social + * graph, where nodes are represented as {@code long} ids, and adjacency lists + * are serialized in efficient manner, for example as {@code long[]} arrays. Typical number + * of connections is 100-300, maximum is 3000. In this case entry size of + * 50 * (8 bytes for each id) = 400 bytes would be a good choice:

    {@code
    +     * Map socialGraph = ChronicleMapBuilder
    +     *     .of(Long.class, long[].class)
    +     *     // given that graph should have of 1 billion nodes, and 150 average adjacency list size
    +     *     // => values takes 3 chuncks on average
    +     *     .entries(1_000_000_000L * (150 / 50))
    +     *     .entrySize(50 * 8)
    +     *     .create();}
    + * It is minimum possible (because 3000 friends / 50 friends = 60 is close to 64 "max + * chunks by single entry" limit, and ensures moderate average memory overuse (not more + * than 20%). + *
  6. + *
+ * + * @param entrySize the "chunk size" in bytes + * @return this builder back + * @see #entries(long) + */ + B entrySize(int entrySize); + + /** + * Configures the maximum number of {@linkplain #entrySize(int) "entry size chunks"}, which + * could be taken by the maximum number of entries, inserted into the hash containers, created + * by this builder. If you try to insert more data, {@link IllegalStateException} might be + * thrown, because currently {@link ChronicleMap} and {@link ChronicleSet} doesn't support + * resizing. + * + *
    + *
  1. If key size (and value size, in {@code ChronicleMap} case) is constant, this number + * is equal to the maximum number of entries (because each entry takes exactly one + * "entry size" memory unit).
  2. + *
  3. If key (and/or value, in {@code ChronicleMap} case) size varies moderately, you + * should pass to this method the maximum number of entries + 5-25%, depending on your data + * properties and configured {@linkplain #keySize(int) key}/{@linkplain + * ChronicleMapBuilder#valueSize(int) value}/{@linkplain #entrySize(int) entry} sizes.
  4. + *
  5. If your data size varies in a wide range, pass the maximum number of entries + * multiplied by average data size and divided by the configured "entry size" (i. e. chunk + * size). See an example in the documentation to {@link #entrySize(int)} method.
  6. + *
+ * + *

You shouldn't put additional margin over the number, computed according the rules above. + * This bad practice was popularized by {@link HashMap#HashMap(int)} + * and {@link HashSet#HashSet(int)} constructors, which accept "capacity", that should + * be multiplied by "load factor" to obtain actual maximum expected number of entries. + * {@code ChronicleMap} and {@code ChronicleSet} don't have a notion of load factor. + * + *

Default value is 2^20 (~ 1 million). + * + * @param entries maximum size of the created maps, in memory allocation units, + * so-called "entry size" + * @return this builder back + * @see #entrySize(int) + */ + B entries(long entries); + + B actualEntriesPerSegment(int actualEntriesPerSegment); + + B actualSegments(int actualSegments); + + B lockTimeOut(long lockTimeOut, TimeUnit unit); + + B errorListener(ChronicleHashErrorListener errorListener); + + B largeSegments(boolean largeSegments); + + B metaDataBytes(int metaDataBytes); + + B addReplicator(Replicator replicator); + + B setReplicators(Replicator... replicators); + + B timeProvider(TimeProvider timeProvider); + + B bytesMarshallerFactory(BytesMarshallerFactory bytesMarshallerFactory); + + B objectSerializer(ObjectSerializer objectSerializer); + + B keyMarshaller(@NotNull BytesMarshaller keyMarshaller); + + /** + * Specifies that key objects, queried with the hash containers, created by this builder, + * are inherently immutable. Keys in {@link ChronicleMap} or {@link ChronicleSet} + * are not required to be immutable, as in ordinary {@link Map} or {@link Set} implementations, + * because they are serialized off-heap. However, {@code ChronicleMap} and {@code ChronicleSet} + * implementations can benefit from the knowledge that keys are not mutated between queries. + * + *

By default, {@code ChronicleHashBuilder}s detects immutability automatically + * only for very few standard JDK types (for example, for {@link String}), it is not recommended + * to rely on {@code ChronicleHashBuilder} to be smart enough about this. + * + * @return this builder back + */ + B immutableKeys(); + + C create(File file) throws IOException; + + C create() throws IOException; +} diff --git a/src/main/java/net/openhft/chronicle/map/MapErrorListener.java b/src/main/java/net/openhft/chronicle/ChronicleHashErrorListener.java similarity index 89% rename from src/main/java/net/openhft/chronicle/map/MapErrorListener.java rename to src/main/java/net/openhft/chronicle/ChronicleHashErrorListener.java index 2517bd5ba..30f404a4e 100644 --- a/src/main/java/net/openhft/chronicle/map/MapErrorListener.java +++ b/src/main/java/net/openhft/chronicle/ChronicleHashErrorListener.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package net.openhft.chronicle.map; +package net.openhft.chronicle; import java.io.Serializable; -public interface MapErrorListener extends Serializable { +public interface ChronicleHashErrorListener extends Serializable { void onLockTimeout(long threadId) throws IllegalStateException; void errorOnUnlock(IllegalMonitorStateException e); diff --git a/src/main/java/net/openhft/chronicle/map/MapErrorListeners.java b/src/main/java/net/openhft/chronicle/ChronicleHashErrorListeners.java similarity index 84% rename from src/main/java/net/openhft/chronicle/map/MapErrorListeners.java rename to src/main/java/net/openhft/chronicle/ChronicleHashErrorListeners.java index 67749d28d..52ff73c4d 100644 --- a/src/main/java/net/openhft/chronicle/map/MapErrorListeners.java +++ b/src/main/java/net/openhft/chronicle/ChronicleHashErrorListeners.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package net.openhft.chronicle.map; +package net.openhft.chronicle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public final class MapErrorListeners { +public final class ChronicleHashErrorListeners { - public static MapErrorListener logging() { + public static ChronicleHashErrorListener logging() { return LoggingErrorListener.INSTANCE; } - public static MapErrorListener error() { + public static ChronicleHashErrorListener error() { return ThrowingErrorListener.INSTANCE; } @@ -35,7 +35,7 @@ public final class MapErrorListeners { * We can add some configuration-on-the-first-call in the future. */ - private static enum LoggingErrorListener implements MapErrorListener { + private static enum LoggingErrorListener implements ChronicleHashErrorListener { INSTANCE; final Logger LOG = LoggerFactory.getLogger(getClass()); @@ -55,7 +55,7 @@ public final class MapErrorListeners { } } - private static enum ThrowingErrorListener implements MapErrorListener { + private static enum ThrowingErrorListener implements ChronicleHashErrorListener { INSTANCE; @Override diff --git a/src/main/java/net/openhft/chronicle/map/TimeProvider.java b/src/main/java/net/openhft/chronicle/TimeProvider.java similarity index 74% rename from src/main/java/net/openhft/chronicle/map/TimeProvider.java rename to src/main/java/net/openhft/chronicle/TimeProvider.java index 0802b5fbd..5f48dcb51 100644 --- a/src/main/java/net/openhft/chronicle/map/TimeProvider.java +++ b/src/main/java/net/openhft/chronicle/TimeProvider.java @@ -14,21 +14,29 @@ * limitations under the License. */ -package net.openhft.chronicle.map; +package net.openhft.chronicle; + +import net.openhft.chronicle.map.ChronicleMap; +import net.openhft.chronicle.map.ChronicleSet; import java.io.ObjectStreamException; import java.io.Serializable; /** - * TimeProvider was created initially to support testing of ReplicatedChronicleMap, - * with the aim of possibly later providing and optimization to System.currentTimeMillis() - * on every call to put(), get() and remove() - *

- * Subclasses should be immutable, because {@link ChronicleMapBuilder} doesn't make defensive copies. + * TimeProvider was aims to possibly later provide an optimization to + * {@link System#currentTimeMillis()} on every call to replicated {@link ChronicleMap} + * or {@link ChronicleSet}. + * + * Subclasses should be immutable, because {@link ChronicleHashBuilder} doesn't make defensive + * copies. * * @author Rob Austin. + * @see ChronicleHashBuilder#timeProvider(TimeProvider) */ public abstract class TimeProvider implements Serializable { + /** + * Delegates {@link #currentTimeMillis()} to {@link System#currentTimeMillis()}. + */ public static final TimeProvider SYSTEM = new System(); private static final long serialVersionUID = 0L; diff --git a/src/main/java/net/openhft/chronicle/map/Alignment.java b/src/main/java/net/openhft/chronicle/map/Alignment.java index 91f9800d9..764cd33d2 100644 --- a/src/main/java/net/openhft/chronicle/map/Alignment.java +++ b/src/main/java/net/openhft/chronicle/map/Alignment.java @@ -18,8 +18,15 @@ package net.openhft.chronicle.map; import net.openhft.lang.io.Bytes; - +/** + * Memory addresses alignment strategies. + * + * @see ChronicleMapBuilder#entryAndValueAlignment(Alignment) + */ public enum Alignment { + /** + * Doesn't align memory addresses. + */ NO_ALIGNMENT { @Override void alignPositionAddr(Bytes bytes) { @@ -37,6 +44,9 @@ public enum Alignment { } }, + /** + * Aligns memory addresses on 4-byte boundary. + */ OF_4_BYTES { @Override void alignPositionAddr(Bytes bytes) { @@ -54,6 +64,9 @@ public enum Alignment { } }, + /** + * Aligns memory addresses on 8-byte boundary. + */ OF_8_BYTES { @Override void alignPositionAddr(Bytes bytes) { diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index d35f0857f..386aeb4cd 100644 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -16,6 +16,10 @@ package net.openhft.chronicle.map; +import net.openhft.chronicle.ChronicleHashBuilder; +import net.openhft.chronicle.ChronicleHashErrorListener; +import net.openhft.chronicle.ChronicleHashErrorListeners; +import net.openhft.chronicle.TimeProvider; import net.openhft.chronicle.map.serialization.MetaBytesInterop; import net.openhft.chronicle.map.serialization.MetaBytesWriter; import net.openhft.chronicle.map.serialization.MetaProvider; @@ -37,11 +41,56 @@ import java.io.*; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; + import java.util.concurrent.TimeUnit; import static net.openhft.chronicle.map.Objects.builderEquals; -public class ChronicleMapBuilder implements Cloneable { +/** + * {@code ChronicleMapBuilder} manages the whole set of {@link ChronicleMap} configurations, could + * be used as a classic builder and/or factory. This means that in addition to the standard builder + * usage pattern:

{@code
+ * ChronicleMap map = ChronicleMapBuilder
+ *     .of(Key.class, Value.class)
+ *     .entries(100500)
+ *     // ... other configurations
+ *     .create();}
+ * {@code ChronicleMapBuilder} could be prepared and used to create many similar maps:
{@code
+ * ChronicleMapBuilder builder = ChronicleMapBuilder
+ *     .of(Key.class, Value.class)
+ *     .entries(100500);
+ *
+ * ChronicleMap map1 = builder.create();
+ * ChronicleMap map2 = builder.create();}
+ * i. e. created {@code ChronicleMap} instances don't depend on the builder. + * + *

Use static {@link #of(Class, Class)} method to obtain a {@code ChronicleMapBuilder} instance. + * + *

{@code ChronicleMapBuilder} is mutable. Configuration methods mutate the builder and + * return the builder itself back to support chaining pattern, rather than the builder copies + * with the corresponding configuration changed. To make an independent configuration, + * {@linkplain #clone} the builder. + * + *

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 should always 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. + * + *

{@code ChronicleMap} allocates memory by equally sized chunks. This size is called + * {@linkplain #entrySize(int) entry size}, you are strongly recommended to configure it to achieve + * least memory consumption and best speed. See {@link #entrySize(int)} method documentation for + * more information on this. + * + * @param key type of the maps, produced by this builder + * @param value type of the maps, produced by this builder + */ +public class ChronicleMapBuilder implements Cloneable, + ChronicleHashBuilder, ChronicleMapBuilder> { private static final Bytes EMPTY_BYTES = new ByteBufferBytes(ByteBuffer.allocate(0)); private static final int DEFAULT_KEY_OR_VALUE_SIZE = 120; @@ -68,7 +117,7 @@ public class ChronicleMapBuilder implements Cloneable { private long lockTimeOut = 2000; private TimeUnit lockTimeOutUnit = TimeUnit.MILLISECONDS; private int metaDataBytes = 0; - private MapErrorListener errorListener = MapErrorListeners.logging(); + private ChronicleHashErrorListener errorListener = ChronicleHashErrorListeners.logging(); private boolean putReturnsNull = false; private boolean removeReturnsNull = false; private boolean largeSegments = false; @@ -111,12 +160,8 @@ public class ChronicleMapBuilder implements Cloneable { } } - /** - * Set minimum number of segments. See concurrencyLevel in {@link java.util.concurrent.ConcurrentHashMap}. - * - * @param minSegments the minimum number of segments in maps, constructed by this builder - * @return this builder object back - */ + + @Override public ChronicleMapBuilder minSegments(int minSegments) { this.minSegments = minSegments; return this; @@ -135,13 +180,7 @@ public class ChronicleMapBuilder implements Cloneable { } /** - * Configures the optimal number of bytes, taken by serialized form of keys, put into maps, created by - * this builder. If key size is always the same, call {@link #constantKeySizeBySample(Object)} method - * instead of this one. - * - *

If key size varies moderately, specify the size higher than average, but lower than the maximum - * possible, to minimize average memory overuse. If key size varies in a wide range, it's better to use - * {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it directly. + * {@inheritDoc} * *

Example: if keys in your map(s) are English words in {@link String} form, keys size 10 * (a bit more than average English word length) would be a good choice:

{@code
@@ -151,16 +190,15 @@ public class ChronicleMapBuilder implements Cloneable {
      *     .keySize(10)
      *     // shouldn't specify valueSize(), because it is statically known
      *     .create();}
- * (Note that 10 is chosen as key size in bytes despite strings in Java are UTF-16 encoded (and each - * character takes 2 bytes on-heap), because default off-heap {@link String} encoding is UTF-8 in {@code - * ChronicleMap}.) + * (Note that 10 is chosen as key size in bytes despite strings in Java are UTF-16 encoded + * (and each character takes 2 bytes on-heap), because default off-heap {@link String} encoding + * is UTF-8 in {@code ChronicleMap}.) * - * @param keySize number of bytes, taken by serialized form of keys - * @return this {@code ChronicleMapBuilder} back * @see #constantKeySizeBySample(Object) * @see #valueSize(int) * @see #entrySize(int) */ + @Override public ChronicleMapBuilder keySize(int keySize) { if (keySize <= 0) throw new IllegalArgumentException("Key size must be positive"); @@ -169,28 +207,18 @@ public class ChronicleMapBuilder implements Cloneable { } /** - * Configures the constant number of bytes, taken by serialized form of keys, put into maps, created by - * this builder. This is done by providing the {@code sampleKey}, all keys should take the same number of - * bytes in serialized form, as this sample object. + * {@inheritDoc} * *

For example, if your keys are Git commit hashes:

{@code
      * Map gitCommitMessagesByHash =
      *     ChronicleMapBuilder.of(byte[].class, String.class)
      *     .constantKeySizeBySample(new byte[20])
-     *     .create();
-     * }
+ * .create();} * - *

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

If key size varies, method {@link #keySize(int)} or {@link #entrySize(int)} should be called instead - * of this one. - * - * @param sampleKey the sample key - * @return this builder back * @see #keySize(int) * @see #constantValueSizeBySample(Object) */ + @Override public ChronicleMapBuilder constantKeySizeBySample(K sampleKey) { this.sampleKey = sampleKey; return this; @@ -201,16 +229,17 @@ public class ChronicleMapBuilder implements Cloneable { } /** - * Configures the optimal 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. + * Configures the optimal 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. * - *

If value is a 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. + *

If value is a 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. * - *

If value size varies moderately, specify the size higher than average, but lower than the maximum - * possible, to minimize average memory overuse. If value size varies in a wide range, it's better to use - * {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it directly. + *

If value size varies moderately, specify the size higher than average, but lower than + * the maximum possible, to minimize average memory overuse. If value size varies in a wide + * range, it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and + * configure it directly. * * @param valueSize number of bytes, taken by serialized form of values * @return this {@code ChronicleMapBuilder} back @@ -225,15 +254,15 @@ 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. + * 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 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 #valueSize(int)} or {@link #entrySize(int)} should be called - * instead of this one. + *

If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be + * called instead of this one. * * @param sampleValue the sample value * @return this builder back @@ -259,70 +288,26 @@ public class ChronicleMapBuilder implements Cloneable { } /** - * Configures the size in bytes of allocation unit of {@code ChronicleMap} instances, created by this - * builder. - * - *

{@code ChronicleMap} stores it's data off-heap, so it is required to serialize key and values - * (unless they are direct {@link Byteable} instances). Serialized key bytes + serialized value bytes + - * some metadata bytes comprise "entry space", which {@code ChronicleMap} should allocate. So entry - * size is a minimum allocation portion in the maps, created by this builder. E. g. if entry size is - * 100, the created map could only allocate 100, 200, 300... bytes for an entry. If say 150 bytes of entry - * space are required by the entry, 200 bytes will be allocated, 150 used and 50 wasted. To minimize - * memory overuse and improve speed, you should pay decent attention to this configuration. + * {@inheritDoc} * *

In fully default case you can expect entry size to be about 256 bytes. But it is strongly - * recommended always to configure {@linkplain #keySize(int) key size} and {@linkplain #valueSize(int) - * value size}, if they couldn't be derived statically. + * recommended always to configure {@linkplain #keySize(int) key size} and + * {@linkplain #valueSize(int) value size}, if they couldn't be derived statically. * - *

If entry size is not {@linkplain #entrySize(int) configured} explicitly, it is computed based on - * {@linkplain #metaDataBytes() meta data bytes}, plus {@linkplain #keySize(int) key size}, plus - * {@linkplain #valueSize(int) value size}, plus a few bytes required by implementations, with respect to - * {@linkplain #entryAndValueAlignment(Alignment) alignment}. - * - *

There are three major patterns of this configuration usage:

  1. Key and value sizes are - * constant. Configure them via {@link #constantKeySizeBySample(Object)} and {@link - * #constantValueSizeBySample(Object)} methods, and you will experience no memory waste at all.
  2. - *
  3. Key and/or value size varies moderately. Specify them using corresponding methods, or {@linkplain - * #entrySize(int) specify entry size} directly, by values somewhere between average and maximum possible. - * The idea is to have most (90% or more) entries to fit a single "entry size" with moderate memory waste - * (10-20% on average), rest 10% or less of entries should take 2 "entry sizes", thus with ~50% memory - * overuse.
  4. Key and/or value size varies in a wide range. Then it's best to use entry size - * configuration in chunk mode. Specify entry size so that most entries should take from 5 to - * several dozens of "chunks". With this approach, average memory waste should be very low. - * - *

    However, remember that

    • Operations with entries that span several "entry sizes" are a bit - * slower, than entries which take a single "entry size". That is why "chunk" approach is not recommended, - * when key and/or value size varies moderately.
    • The maximum number of chunks could be taken by - * an entry is 64. {@link IllegalArgumentException} is thrown on attempt to insert too large entry, - * compared to the configured or computed entry size.
    • The number of "entries" {@linkplain - * #entries(long) in the whole map} and {@linkplain #actualEntriesPerSegment(int) per segment} is actually - * the number "chunks".
    - * - *

    Example: if values in your map are adjacency lists of some social graph, where nodes are represented - * as {@code long} ids, and adjacency lists are serialized in efficient manner, for example as {@code - * long[]} arrays. Typical number of connections is 100-300, maximum is 3000. In this case entry size of - * 50 * (8 bytes for each id) = 400 bytes would - * be a good choice:

    {@code
    -     * Map socialGraph = ChronicleMapBuilder
    -     *     .of(Long.class, long[].class)
    -     *     // given that graph should have of 1 billion nodes, and 150 average adjacency list size
    -     *     // => values takes 3 chuncks on average
    -     *     .entries(1_000_000_000L * (150 / 50))
    -     *     .entrySize(50 * 8)
    -     *     .create();}
    - * It is minimum possible (because 3000 friends / 50 friends = 60 is close to 64 "max chunks by single - * entry" limit, and ensures moderate average memory overuse (not more than 20%).
+ *

If entry size is not configured explicitly by calling this method, it is computed + * based on {@linkplain #metaDataBytes(int) meta data bytes}, plus {@linkplain #keySize(int) key + * size}, plus {@linkplain #valueSize(int) value size}, plus a few bytes required by + * implementations, with respect to {@linkplain #entryAndValueAlignment(Alignment) alignment}. * *

Note that the actual entrySize will be aligned to 4 (default {@linkplain - * #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, the actual - * entry size will be 32 (30 aligned to 4 bytes). If you don't want entry size to be aligned, set {@code - * entryAndValueAlignment(Alignment.NO_ALIGNMENT)}. + * #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, + * the actual entry size will be 32 (30 aligned to 4 bytes). If you don't want entry size + * to be aligned, set {@code entryAndValueAlignment(Alignment.NO_ALIGNMENT)}. * - * @param entrySize the size in bytes - * @return this {@code ChronicleMapBuilder} back * @see #entryAndValueAlignment(Alignment) * @see #entries(long) */ + @Override public ChronicleMapBuilder entrySize(int entrySize) { if (entrySize <= 0) throw new IllegalArgumentException("Entry Size must be positive"); @@ -359,16 +344,16 @@ 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. + * 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. + *

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. * - *

Note that {@linkplain #entrySize(int) entry size} will be aligned according to this alignment. I. e. - * if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual entry size will be 24 (20 - * aligned to 8 bytes). + *

Note that {@linkplain #entrySize(int) entry size} will be aligned according to this + * alignment. I. e. if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual + * entry size will be 24 (20 aligned to 8 bytes). * *

Default is {@link Alignment#OF_4_BYTES}. * @@ -384,32 +369,7 @@ public class ChronicleMapBuilder implements Cloneable { return alignment; } - /** - * Configures the maximum number of {@linkplain #entrySize(int) "entry size chunks"}, which could be taken - * by the maximum number of entries, inserted into the maps, created by this builder. If you try to insert - * more data, {@link IllegalStateException} might be thrown, because currently {@code ChronicleMap} - * doesn't support resizing. - * - *

  1. If key and value sizes are constant, this number is equal to the maximum number of entries - * (because each entry takes exactly one "entry size" memory unit).
  2. If key and/or value size - * varies moderately, you should pass to this method the maximum number of entries + 5-25%, depending on - * your data properties and configured {@linkplain #keySize(int) key}/{@linkplain #valueSize(int) - * value}/{@linkplain #entrySize(int) entry} sizes.
  3. If your data size varies in a wide range, - * pass the maximum number of entries multiplied by average data size and divided by the configured "entry - * size" (i. e. chunk size). See an example in the documentation to {@link #entrySize(int)} method.
  4. - *
- * - *

You shouldn't put additional margin over the number, computed according the rules above. This bad - * practice was popularized by {@link HashMap#HashMap(int)} constructor, which accept "capacity", that - * should be multiplied by "load factor" to obtain actual maximum expected number of entries. {@code - * ChronicleMap} doesn't have a notion of load factor. - * - *

Default value is 2^20 (~ 1 million). - * - * @param entries maximum size of the created maps, in memory allocation units, so-called "entry size" - * @return this builder back - * @see #entrySize(int) - */ + @Override public ChronicleMapBuilder entries(long entries) { this.entries = entries; return this; @@ -419,6 +379,7 @@ public class ChronicleMapBuilder implements Cloneable { return entries; } + @Override public ChronicleMapBuilder actualEntriesPerSegment(int actualEntriesPerSegment) { this.actualEntriesPerSegment = actualEntriesPerSegment; return this; @@ -432,6 +393,7 @@ public class ChronicleMapBuilder implements Cloneable { return (int) (Math.max(1, entries * 2L / as) + 63) & ~63; } + @Override public ChronicleMapBuilder actualSegments(int actualSegments) { this.actualSegments = actualSegments; return this; @@ -459,30 +421,31 @@ public class ChronicleMapBuilder implements Cloneable { return unit.convert(lockTimeOut, lockTimeOutUnit); } - public ChronicleMapBuilder errorListener(MapErrorListener errorListener) { + @Override + public ChronicleMapBuilder errorListener(ChronicleHashErrorListener errorListener) { this.errorListener = errorListener; return this; } - MapErrorListener errorListener() { + ChronicleHashErrorListener errorListener() { return errorListener; } /** - * Configures if the maps created by this builder should return {@code null} instead of previous mapped - * values on {@link ChronicleMap#put(Object, Object) ChornicleMap.put(key, value)} calls. + * Configures if the maps created by this builder should return {@code null} instead of previous + * mapped values on {@link ChronicleMap#put(Object, Object) ChornicleMap.put(key, value)} calls. * - *

{@link Map#put(Object, Object) Map.put()} returns the previous value, functionality which is rarely - * used but fairly cheap for {@link HashMap}. In the case, for an off heap collection, it has to create a - * new object and deserialize the data from off-heap memory. It's expensive for something you probably - * don't use. + *

{@link Map#put(Object, Object) Map.put()} returns the previous value, functionality which + * is rarely used but fairly cheap for {@link HashMap}. In the case, for an off heap collection, + * it has to create a new object and deserialize the data from off-heap memory. It's expensive + * for something you probably don't use. * - *

By default, of cause, {@code ChronicleMap} conforms the general {@code Map} contract and returns the - * previous mapped value on {@code put()} calls. + *

By default, of cause, {@code ChronicleMap} conforms the general {@code Map} contract + * and returns the previous mapped value on {@code put()} calls. * * @param putReturnsNull {@code true} if you want {@link ChronicleMap#put(Object, Object) - * ChronicleMap.put()} to not return the value that was replaced but instead return - * {@code null} + * ChronicleMap.put()} to not return the value that was replaced + * but instead return {@code null} * @return an instance of the map builder back * @see #removeReturnsNull(boolean) */ @@ -496,17 +459,17 @@ public class ChronicleMapBuilder implements Cloneable { } /** - * Configures if the maps created by this builder should return {@code null} instead of the last mapped - * value on {@link ChronicleMap#remove(Object) ChronicleMap.remove(key)} calls. + * Configures if the maps created by this builder should return {@code null} instead of + * the last mapped value on {@link ChronicleMap#remove(Object) ChronicleMap.remove(key)} calls. * - *

{@link Map#remove(Object) Map.remove()} returns the previous value, functionality which is rarely - * used but fairly cheap for {@link HashMap}. In the case, for an off heap collection, it has to create a - * new object and deserialize the data from off-heap memory. It's expensive for something you probably - * don't use. + *

{@link Map#remove(Object) Map.remove()} returns the previous value, functionality which + * is rarely used but fairly cheap for {@link HashMap}. In the case, for an off heap collection, + * it has to create a new object and deserialize the data from off-heap memory. It's expensive + * for something you probably don't use. * * @param removeReturnsNull {@code true} if you want {@link ChronicleMap#remove(Object) - * ChronicleMap.remove()} to not return the value of the removed entry but - * instead return {@code null} + * ChronicleMap.remove()} to not return the value of the removed entry + * but instead return {@code null} * @return an instance of the map builder back * @see #putReturnsNull(boolean) */ @@ -523,11 +486,13 @@ public class ChronicleMapBuilder implements Cloneable { return entries > 1L << (20 + 15) || largeSegments; } + @Override public ChronicleMapBuilder largeSegments(boolean largeSegments) { this.largeSegments = largeSegments; return this; } + @Override public ChronicleMapBuilder metaDataBytes(int metaDataBytes) { if ((metaDataBytes & 0xFF) != metaDataBytes) throw new IllegalArgumentException("MetaDataBytes must be [0..255] was " + metaDataBytes); @@ -608,6 +573,7 @@ public class ChronicleMapBuilder implements Cloneable { return this; } + @Override public ChronicleMapBuilder timeProvider(TimeProvider timeProvider) { this.timeProvider = timeProvider; return this; @@ -623,6 +589,7 @@ public class ChronicleMapBuilder implements Cloneable { bytesMarshallerFactory; } + @Override public ChronicleMapBuilder bytesMarshallerFactory(BytesMarshallerFactory bytesMarshallerFactory) { this.bytesMarshallerFactory = bytesMarshallerFactory; return this; @@ -635,27 +602,20 @@ public class ChronicleMapBuilder implements Cloneable { objectSerializer; } + @Override public ChronicleMapBuilder objectSerializer(ObjectSerializer objectSerializer) { this.objectSerializer = objectSerializer; return this; } + + @Override public ChronicleMapBuilder keyMarshaller(@NotNull BytesMarshaller keyMarshaller) { keyBuilder.marshaller(keyMarshaller, null); return this; } - /** - * Specifies that key objects, queried with the maps created by this builder, are inherently immutable. - * Keys in {@code ChronicleMap} are not required to be immutable, as in ordinary {@link Map} - * implementations, because they are serialized off-heap. However, {@code ChronicleMap} implementations - * can benefit from the knowledge that keys are not mutated between queries. By default, {@code - * ChronicleMapBuilder} detects immutability automatically only for very few standard JDK types (for - * example, for {@link String}), it is not recommended to rely on {@code ChronicleMapBuilder} to be smart - * enough about this. - * - * @return this builder back - */ + @Override public ChronicleMapBuilder immutableKeys() { keyBuilder.instancesAreMutable(false); return this; @@ -683,18 +643,19 @@ public class ChronicleMapBuilder implements Cloneable { } /** - * Specifies the value to be put for each key queried in {@link ChronicleMap#get get()} and {@link - * ChronicleMap#getUsing(Object, Object) getUsing()} methods, if the key is absent in the map. Then this - * default value is returned from query method. + * Specifies the value to be put for each key queried in {@link ChronicleMap#get get()} and + * {@link ChronicleMap#getUsing(Object, Object) getUsing()} methods, if the key is absent + * in the map. Then this default value is returned from query method. * - *

Setting default value to {@code null} is interpreted as map shouldn't put any default value for - * absent keys. This is by default. + *

Setting default value to {@code null} is interpreted as map shouldn't put any default + * value for absent keys. This is by default. * - *

This configuration overrides any previous {@link #defaultValueProvider(DefaultValueProvider)} - * configuration to this {@code ChronicleMapBuilder}. + *

This configuration overrides any previous + * {@link #defaultValueProvider(DefaultValueProvider)} configuration to this + * {@code ChronicleMapBuilder}. * - * @param defaultValue the default value to be put to the map for absent keys during {@code get()} and - * {@code getUsing()} calls and returned from these calls + * @param defaultValue the default value to be put to the map for absent keys during + * {@code get()} and {@code getUsing()} calls and returned from these calls * @return this builder object back */ public ChronicleMapBuilder defaultValue(V defaultValue) { @@ -704,13 +665,13 @@ public class ChronicleMapBuilder implements Cloneable { } /** - * Specifies the function to obtain a value for the key during {@link ChronicleMap#get get()} and {@link - * ChronicleMap#getUsing(Object, Object) getUsing()} calls, if the key is absent in the map. If the - * obtained value is non-null, it is put for the key in the map and then returned from current {@code - * get()} or {@code getUsing()} call. + * Specifies the function to obtain a value for the key during {@link ChronicleMap#get get()} + * and {@link ChronicleMap#getUsing(Object, Object) getUsing()} calls, if the key is absent + * in the map. If the obtained value is non-null, it is put for the key in the map and then + * returned from current {@code get()} or {@code getUsing()} call. * - *

This configuration overrides any previous {@link #defaultValue(Object)} configuration to this {@code - * ChronicleMapBuilder}. + *

This configuration overrides any previous {@link #defaultValue(Object)} configuration + * to this {@code ChronicleMapBuilder}. * * @param defaultValueProvider the strategy to obtain a default value by the absent key * @return this builder object back @@ -740,6 +701,7 @@ public class ChronicleMapBuilder implements Cloneable { return new ConstantValueProvider(defaultValue, metaValueWriter, valueWriter); } + @Override public ChronicleMap create(File file) throws IOException { for (int i = 0; i < 10; i++) { if (file.exists() && file.length() > 0) { @@ -786,6 +748,7 @@ public class ChronicleMapBuilder implements Cloneable { return establishReplication(map); } + @Override public ChronicleMap create() throws IOException { VanillaChronicleMap map = newMap(); BytesStore bytesStore = new DirectStore(JDKObjectSerializer.INSTANCE, diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index ec60352ba..1a2189a09 100644 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -16,6 +16,7 @@ package net.openhft.chronicle.map; +import net.openhft.chronicle.ChronicleHashErrorListener; import net.openhft.chronicle.map.serialization.*; import net.openhft.chronicle.map.threadlocal.Provider; import net.openhft.chronicle.map.threadlocal.ThreadLocalCopies; @@ -88,7 +89,7 @@ class VanillaChronicleMap, final boolean removeReturnsNull; private final long lockTimeOutNS; - private final MapErrorListener errorListener; + private final ChronicleHashErrorListener errorListener; transient Segment[] segments; // non-final for close() // non-final for close() and because it is initialized out of constructor transient BytesStore ms; diff --git a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java index 34754c425..251b8c000 100644 --- a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java +++ b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java @@ -16,6 +16,10 @@ package net.openhft.chronicle.set; +import net.openhft.chronicle.TimeProvider; +import net.openhft.chronicle.map.Alignment; +import net.openhft.chronicle.ChronicleHashBuilder; +import net.openhft.chronicle.ChronicleHashErrorListener; import net.openhft.chronicle.map.*; import net.openhft.lang.io.serialization.BytesMarshaller; import net.openhft.lang.io.serialization.BytesMarshallerFactory; @@ -29,10 +33,12 @@ import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -public class ChronicleSetBuilder implements Cloneable { +public class ChronicleSetBuilder + implements ChronicleHashBuilder, ChronicleSetBuilder> { private ChronicleMapBuilder chronicleMapBuilder; @@ -57,98 +63,104 @@ public class ChronicleSetBuilder implements Cloneable { } } + @Override public ChronicleSetBuilder actualSegments(int actualSegments) { chronicleMapBuilder.actualSegments(actualSegments); return this; } - /** - * Set minimum number of segments. See concurrencyLevel in {@link java.util.concurrent.ConcurrentHashMap}. - * - * @param minSegments the minimum number of segments in maps, constructed by this builder - * @return this builder object back - */ + @Override public ChronicleSetBuilder minSegments(int minSegments) { chronicleMapBuilder.minSegments(minSegments); return this; } - public ChronicleSetBuilder actualElementsPerSegment(int actualEntriesPerSegment) { + @Override + public ChronicleSetBuilder actualEntriesPerSegment(int actualEntriesPerSegment) { chronicleMapBuilder.actualEntriesPerSegment(actualEntriesPerSegment); return this; } /** - * Configures the optimal number of bytes for an element. If elementSize is always the same, call {@link - * #constantElementsSizeBySample(Object)} method instead of this one. + * {@inheritDoc} * - *

If the size varies moderately, specify the size higher than average, but lower than the maximum - * possible, to minimize average memory overuse. If the size varies in a wide range, it's better to use - * - * - *

Example: if element in your set(s) are English words in {@link String} form, keys size 10 + *

Example: if keys in your set(s) are English words in {@link String} form, keys size 10 * (a bit more than average English word length) would be a good choice:

{@code
-     * ChronicleSetBuilder wordFrequencies = ChronicleSetBuilder
-     *     .of(String.class)
+     * ChronicleSet uniqueWords = ChronicleSetBuilder.of(String.class)
      *     .entries(50000)
-     *     .elementSize(10)
+     *     .keySize(10)
      *     .create();}
+ * (Note that 10 is chosen as key size in bytes despite strings in Java are UTF-16 encoded + * (and each character takes 2 bytes on-heap), because default off-heap {@link String} encoding + * is UTF-8 in {@code ChronicleSet}.) * - * @param elementSize number of bytes, taken by serialized form of the element - * @return this {@code ChronicleSetBuilder} back - * @see #constantElementsSizeBySample(Object) + * @see #constantKeySizeBySample(Object) + * @see #entrySize(int) */ - public ChronicleSetBuilder elementSize(int elementSize) { - chronicleMapBuilder.keySize(elementSize); + @Override + public ChronicleSetBuilder keySize(int keySize) { + chronicleMapBuilder.keySize(keySize); return this; } - /** - * Configures the constant number of bytes, taken by serialized form of elements, created by this builder. - * This is done by providing the {@code element}, all elements should take the same number of bytes in - * serialized form, as this sample object. + * {@inheritDoc} * - *

For example, if your elements are Git commit hashes:

{@code
-     * Map gitCommitMessagesByHash =
-     *     ChronicleSetBuilder.of(byte[].class)
-     *     .constantElementsSizeBySample(new byte[20])
-     *     .create();
-     * }
+ *

For example, if your keys are Git commit hashes:

{@code
+     * Set gitCommitsOfInterest = ChronicleSetBuilder.of(byte[].class)
+     *     .constantKeySizeBySample(new byte[20])
+     *     .create();}
* - *

If elements are of boxed primitive type or {@link net.openhft.lang.model.Byteable} subclass, i. e. - * if element size is known statically, it is automatically accounted and this method shouldn't be - * called. - * - * @param element the sample element - * @return this builder back + * @see #keySize(int) */ - public ChronicleSetBuilder constantElementsSizeBySample(E element) { - chronicleMapBuilder.constantKeySizeBySample(element); + @Override + public ChronicleSetBuilder constantKeySizeBySample(E sampleKey) { + chronicleMapBuilder.constantKeySizeBySample(sampleKey); return this; } - public ChronicleSetBuilder elements(long entries) { + /** + * {@inheritDoc} + * + *

In fully default case you can expect entry size to be about 120-130 bytes. But it is + * strongly recommended always to configure {@linkplain #keySize(int) key size}, + * if they couldn't be derived statically. + * + *

If entry size is not configured explicitly by calling this method, it is computed + * based on {@linkplain #metaDataBytes(int) meta data bytes}, plus {@linkplain #keySize(int) key + * size}, plus a few bytes required by implementations. + */ + @Override + public ChronicleSetBuilder entrySize(int entrySize) { + chronicleMapBuilder.entrySize(entrySize); + return this; + } + + @Override + public ChronicleSetBuilder entries(long entries) { chronicleMapBuilder.entries(entries); return this; } + @Override public ChronicleSetBuilder lockTimeOut(long lockTimeOut, TimeUnit unit) { chronicleMapBuilder.lockTimeOut(lockTimeOut, unit); return this; } - - public ChronicleSetBuilder errorListener(MapErrorListener errorListener) { + @Override + public ChronicleSetBuilder errorListener(ChronicleHashErrorListener errorListener) { chronicleMapBuilder.errorListener(errorListener); return this; } + @Override public ChronicleSetBuilder largeSegments(boolean largeSegments) { chronicleMapBuilder.largeSegments(largeSegments); return this; } + @Override public ChronicleSetBuilder metaDataBytes(int metaDataBytes) { chronicleMapBuilder.metaDataBytes(metaDataBytes); return this; @@ -164,7 +176,9 @@ public class ChronicleSetBuilder implements Cloneable { @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override public boolean equals(Object o) { - return chronicleMapBuilder.equals(o); + if (!(o instanceof ChronicleSetBuilder)) + return false; + return chronicleMapBuilder.equals(((ChronicleSetBuilder) o).chronicleMapBuilder); } @Override @@ -178,45 +192,44 @@ public class ChronicleSetBuilder implements Cloneable { } + @Override public ChronicleSetBuilder timeProvider(TimeProvider timeProvider) { chronicleMapBuilder.timeProvider(timeProvider); return this; } - public ChronicleSetBuilder bytesMarshallerFactory(BytesMarshallerFactory bytesMarshallerFactory) { + @Override + public ChronicleSetBuilder bytesMarshallerFactory( + BytesMarshallerFactory bytesMarshallerFactory) { chronicleMapBuilder.bytesMarshallerFactory(bytesMarshallerFactory); return this; } + @Override public ChronicleSetBuilder objectSerializer(ObjectSerializer objectSerializer) { chronicleMapBuilder.objectSerializer(objectSerializer); return this; } + @Override public ChronicleSetBuilder keyMarshaller(@NotNull BytesMarshaller keyMarshaller) { chronicleMapBuilder.keyMarshaller(keyMarshaller); return this; } - /** - * Specifies that elements, are immutable. {@code ChronicleSet} implementations can benefit from the - * knowledge that elements are not mutated between queries. By default, {@code ChronicleMapBuilder} - * detects immutability automatically only for very few standard JDK types (for example, for {@link - * String}), it is not recommended to rely on {@code ChronicleSetBuilder} to be smart enough about this. - * - * @return this builder back - */ + @Override public ChronicleSetBuilder immutableKeys() { chronicleMapBuilder.immutableKeys(); return this; } - + @Override public ChronicleSet create(File file) throws IOException { final ChronicleMap map = chronicleMapBuilder.create(file); return new SetFromMap(map); } + @Override public ChronicleSet create() throws IOException { final ChronicleMap map = chronicleMapBuilder.create(); return new SetFromMap(map); diff --git a/src/test/java/net/openhft/chronicle/map/TimeBasedReplicationTests.java b/src/test/java/net/openhft/chronicle/map/TimeBasedReplicationTests.java index 6bd5beb8e..51d1b54f8 100644 --- a/src/test/java/net/openhft/chronicle/map/TimeBasedReplicationTests.java +++ b/src/test/java/net/openhft/chronicle/map/TimeBasedReplicationTests.java @@ -16,6 +16,7 @@ package net.openhft.chronicle.map; +import net.openhft.chronicle.TimeProvider; import net.openhft.chronicle.map.jrs166.JSR166TestCase; import org.junit.Test; import org.mockito.Mockito;