diff --git a/README.md b/README.md index cbb2abe..8d674f0 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,14 @@ _Note_: currently the lib contains wrappers not for every primitive map. Feel fr com.trivago fastutil-concurrent-wrapper - 0.2.2 + 0.2.3 ``` #### Gradle ```groovy -implementation group: 'com.trivago', name: 'fastutil-concurrent-wrapper', version: '0.2.2' +implementation group: 'com.trivago', name: 'fastutil-concurrent-wrapper', version: '0.2.3' ``` ## Usage @@ -135,3 +135,4 @@ A-Z surname order - [@mchernyakov](https://github.com/mchernyakov) - [@erdoganf](https://github.com/erdoganf) - [@sarveswaran-m](https://github.com/sarveswaran-m) +- [@p0nkr4t](https://github.com/p0nkr4t) diff --git a/build.gradle b/build.gradle index 1c8da32..c6e4010 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ allprojects { } group 'com.trivago' -version '0.2.3-SNAPSHOT' +version '0.2.4-SNAPSHOT' mavenPublishing { pom { @@ -46,6 +46,11 @@ mavenPublishing { name = "Sarveswaran Meenakshisundaram" url = "https://github.com/sarveswaran-m" } + developer { + id = "p0nkr4t" + name = "Ivan Korovin" + url = "https://github.com/p0nkr4t" + } } scm { url = "https://github.com/trivago/fastutil-concurrent-wrapper" diff --git a/src/main/java/com/trivago/fastutilconcurrentwrapper/ConcurrentIntLongMapBuilder.java b/src/main/java/com/trivago/fastutilconcurrentwrapper/ConcurrentIntLongMapBuilder.java new file mode 100644 index 0000000..470f240 --- /dev/null +++ b/src/main/java/com/trivago/fastutilconcurrentwrapper/ConcurrentIntLongMapBuilder.java @@ -0,0 +1,74 @@ +package com.trivago.fastutilconcurrentwrapper; + +import com.trivago.fastutilconcurrentwrapper.map.ConcurrentBusyWaitingIntLongMap; +import com.trivago.fastutilconcurrentwrapper.map.ConcurrentIntLongMap; + +public final class ConcurrentIntLongMapBuilder { + private MapMode mapMode = MapMode.BLOCKING; + private int buckets = 8; + private int initialCapacity = 100_000; + private float loadFactor = 0.8f; + private long defaultValue = IntLongMap.DEFAULT_VALUE; + + private ConcurrentIntLongMapBuilder() { + + } + + public static ConcurrentIntLongMapBuilder newBuilder() { + return new ConcurrentIntLongMapBuilder(); + } + + public ConcurrentIntLongMapBuilder withBuckets(int buckets) { + this.buckets = buckets; + return this; + } + + public ConcurrentIntLongMapBuilder withInitialCapacity(int initialCapacity) { + this.initialCapacity = initialCapacity; + return this; + } + + public ConcurrentIntLongMapBuilder withLoadFactor(float loadFactor) { + this.loadFactor = loadFactor; + return this; + } + + public ConcurrentIntLongMapBuilder withMode(MapMode mapMode) { + this.mapMode = mapMode; + return this; + } + + public ConcurrentIntLongMapBuilder withDefaultValue(long defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + public IntLongMap build() { + return mapMode.createMap(this); + } + + public enum MapMode { + BUSY_WAITING { + @Override + IntLongMap createMap(ConcurrentIntLongMapBuilder builder) { + return new ConcurrentBusyWaitingIntLongMap( + builder.buckets, + builder.initialCapacity, + builder.loadFactor, + builder.defaultValue); + } + }, + BLOCKING { + @Override + IntLongMap createMap(ConcurrentIntLongMapBuilder builder) { + return new ConcurrentIntLongMap( + builder.buckets, + builder.initialCapacity, + builder.loadFactor, + builder.defaultValue); + } + }; + + abstract IntLongMap createMap(ConcurrentIntLongMapBuilder builder); + } +} diff --git a/src/main/java/com/trivago/fastutilconcurrentwrapper/IntLongMap.java b/src/main/java/com/trivago/fastutilconcurrentwrapper/IntLongMap.java new file mode 100644 index 0000000..08a3ad4 --- /dev/null +++ b/src/main/java/com/trivago/fastutilconcurrentwrapper/IntLongMap.java @@ -0,0 +1,28 @@ +package com.trivago.fastutilconcurrentwrapper; + +import it.unimi.dsi.fastutil.ints.Int2LongFunction; + +import java.util.function.BiFunction; + +public interface IntLongMap extends PrimitiveIntKeyMap { + + long DEFAULT_VALUE = 0; + + /** + * @param key + * @return 0 if the key is not present + */ + long get(int key); + + long put(int key, long value); + + long getDefaultValue(); + + long remove(int key); + + boolean remove(int key, long value); + + long computeIfAbsent(int key, Int2LongFunction mappingFunction); + + long computeIfPresent(int key, BiFunction mappingFunction); +} diff --git a/src/main/java/com/trivago/fastutilconcurrentwrapper/map/ConcurrentBusyWaitingIntLongMap.java b/src/main/java/com/trivago/fastutilconcurrentwrapper/map/ConcurrentBusyWaitingIntLongMap.java new file mode 100644 index 0000000..7bad306 --- /dev/null +++ b/src/main/java/com/trivago/fastutilconcurrentwrapper/map/ConcurrentBusyWaitingIntLongMap.java @@ -0,0 +1,162 @@ +package com.trivago.fastutilconcurrentwrapper.map; + +import com.trivago.fastutilconcurrentwrapper.IntLongMap; +import com.trivago.fastutilconcurrentwrapper.wrapper.PrimitiveFastutilIntLongWrapper; +import it.unimi.dsi.fastutil.ints.Int2LongFunction; + +import java.util.concurrent.locks.Lock; +import java.util.function.BiFunction; + +public class ConcurrentBusyWaitingIntLongMap extends PrimitiveConcurrentMap implements IntLongMap { + + private final IntLongMap[] maps; + private final long defaultValue; + + public ConcurrentBusyWaitingIntLongMap(int numBuckets, + int initialCapacity, + float loadFactor, + long defaultValue) { + super(numBuckets); + + this.maps = new IntLongMap[numBuckets]; + this.defaultValue = defaultValue; + + for (int i = 0; i < numBuckets; i++) { + maps[i] = new PrimitiveFastutilIntLongWrapper(initialCapacity, loadFactor, defaultValue); + } + } + + @Override + public int size() { + return super.size(maps); + } + + @Override + public boolean isEmpty() { + return super.isEmpty(maps); + } + + @Override + public boolean containsKey(int key) { + int bucket = getBucket(key); + + Lock readLock = locks[bucket].readLock(); + + while (true) { + if (readLock.tryLock()) { + try { + return maps[bucket].containsKey(key); + } finally { + readLock.unlock(); + } + } + } + } + + @Override + public long get(int key) { + int bucket = getBucket(key); + + Lock readLock = locks[bucket].readLock(); + + while (true) { + if (readLock.tryLock()) { + try { + return maps[bucket].get(key); + } finally { + readLock.unlock(); + } + } + } + } + + @Override + public long put(int key, long value) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + + while (true) { + if (writeLock.tryLock()) { + try { + return maps[bucket].put(key, value); + } finally { + writeLock.unlock(); + } + } + } + } + + @Override + public long getDefaultValue() { + return defaultValue; + } + + @Override + public long remove(int key) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + + while (true) { + if (writeLock.tryLock()) { + try { + return maps[bucket].remove(key); + } finally { + writeLock.unlock(); + } + } + } + } + + @Override + public boolean remove(int key, long value) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + + while (true) { + if (writeLock.tryLock()) { + try { + return maps[bucket].remove(key, value); + } finally { + writeLock.unlock(); + } + } + } + } + + @Override + public long computeIfAbsent(int key, Int2LongFunction mappingFunction) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + + while (true) { + if (writeLock.tryLock()) { + try { + return maps[bucket].computeIfAbsent(key, mappingFunction); + } finally { + writeLock.unlock(); + } + } + } + } + + @Override + public long computeIfPresent(int key, BiFunction mappingFunction) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + + while (true) { + if (writeLock.tryLock()) { + try { + return maps[bucket].computeIfPresent(key, mappingFunction); + } finally { + writeLock.unlock(); + } + } + } + } +} diff --git a/src/main/java/com/trivago/fastutilconcurrentwrapper/map/ConcurrentIntLongMap.java b/src/main/java/com/trivago/fastutilconcurrentwrapper/map/ConcurrentIntLongMap.java new file mode 100644 index 0000000..2844d35 --- /dev/null +++ b/src/main/java/com/trivago/fastutilconcurrentwrapper/map/ConcurrentIntLongMap.java @@ -0,0 +1,142 @@ +package com.trivago.fastutilconcurrentwrapper.map; + +import com.trivago.fastutilconcurrentwrapper.IntLongMap; +import com.trivago.fastutilconcurrentwrapper.wrapper.PrimitiveFastutilIntLongWrapper; +import it.unimi.dsi.fastutil.ints.Int2LongFunction; + +import java.util.concurrent.locks.Lock; +import java.util.function.BiFunction; + +public class ConcurrentIntLongMap extends PrimitiveConcurrentMap implements IntLongMap { + + private final IntLongMap[] maps; + private final long defaultValue; + + public ConcurrentIntLongMap(int numBuckets, + int initialCapacity, + float loadFactor, + long defaultValue) { + super(numBuckets); + + this.maps = new IntLongMap[numBuckets]; + this.defaultValue = defaultValue; + + for (int i = 0; i < numBuckets; i++) { + maps[i] = new PrimitiveFastutilIntLongWrapper(initialCapacity, loadFactor, defaultValue); + } + } + + @Override + public int size() { + return super.size(maps); + } + + @Override + public boolean isEmpty() { + return super.isEmpty(maps); + } + + @Override + public boolean containsKey(int key) { + int bucket = getBucket(key); + + Lock readLock = locks[bucket].readLock(); + readLock.lock(); + try { + return maps[bucket].containsKey(key); + } finally { + readLock.unlock(); + } + } + + @Override + public long get(int l) { + int bucket = getBucket(l); + + long result; + + Lock readLock = locks[bucket].readLock(); + readLock.lock(); + try { + result = maps[bucket].get(l); + } finally { + readLock.unlock(); + } + + return result; + } + + @Override + public long put(int key, long value) { + int bucket = getBucket(key); + + long result; + + Lock writeLock = locks[bucket].writeLock(); + writeLock.lock(); + try { + result = maps[bucket].put(key, value); + } finally { + writeLock.unlock(); + } + + return result; + } + + @Override + public long getDefaultValue() { + return defaultValue; + } + + @Override + public long remove(int key) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + writeLock.lock(); + try { + return maps[bucket].remove(key); + } finally { + writeLock.unlock(); + } + } + + @Override + public boolean remove(int key, long value) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + writeLock.lock(); + try { + return maps[bucket].remove(key, value); + } finally { + writeLock.unlock(); + } + } + + @Override + public long computeIfAbsent(int key, Int2LongFunction mappingFunction) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + writeLock.lock(); + try { + return maps[bucket].computeIfAbsent(key, mappingFunction); + } finally { + writeLock.unlock(); + } + } + + @Override + public long computeIfPresent(int key, BiFunction mappingFunction) { + int bucket = getBucket(key); + + Lock writeLock = locks[bucket].writeLock(); + writeLock.lock(); + try { + return maps[bucket].computeIfPresent(key, mappingFunction); + } finally { + writeLock.unlock(); + } + } +} diff --git a/src/main/java/com/trivago/fastutilconcurrentwrapper/wrapper/PrimitiveFastutilIntLongWrapper.java b/src/main/java/com/trivago/fastutilconcurrentwrapper/wrapper/PrimitiveFastutilIntLongWrapper.java new file mode 100644 index 0000000..1495d39 --- /dev/null +++ b/src/main/java/com/trivago/fastutilconcurrentwrapper/wrapper/PrimitiveFastutilIntLongWrapper.java @@ -0,0 +1,71 @@ +package com.trivago.fastutilconcurrentwrapper.wrapper; + +import com.trivago.fastutilconcurrentwrapper.IntLongMap; +import it.unimi.dsi.fastutil.ints.Int2LongFunction; +import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap; + +import java.util.function.BiFunction; + +public class PrimitiveFastutilIntLongWrapper implements IntLongMap { + private final Int2LongOpenHashMap map; + private final long defaultValue; + + public PrimitiveFastutilIntLongWrapper(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, IntLongMap.DEFAULT_VALUE); + } + + public PrimitiveFastutilIntLongWrapper(int initialCapacity, float loadFactor, long defaultValue) { + this.defaultValue = defaultValue; + this.map = new Int2LongOpenHashMap(initialCapacity, loadFactor); + } + + @Override + public long get(int key) { + return map.getOrDefault(key, defaultValue); + } + + @Override + public long put(int key, long value) { + return map.put(key, value); + } + + @Override + public long getDefaultValue() { + return defaultValue; + } + + @Override + public long remove(int key) { + return map.remove(key); + } + + @Override + public boolean remove(int key, long value) { + return map.remove(key, value); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean containsKey(int key) { + return map.containsKey(key); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public long computeIfAbsent(int key, Int2LongFunction mappingFunction) { + return map.computeIfAbsent(key, mappingFunction); + } + + @Override + public long computeIfPresent(int key, BiFunction mappingFunction) { + return map.computeIfPresent(key, mappingFunction); + } +} diff --git a/src/test/java/com/trivago/fastutilconcurrentwrapper/ConcurrentIntLongMapBuilderTest.java b/src/test/java/com/trivago/fastutilconcurrentwrapper/ConcurrentIntLongMapBuilderTest.java new file mode 100644 index 0000000..80b306a --- /dev/null +++ b/src/test/java/com/trivago/fastutilconcurrentwrapper/ConcurrentIntLongMapBuilderTest.java @@ -0,0 +1,49 @@ +package com.trivago.fastutilconcurrentwrapper; + +import com.trivago.fastutilconcurrentwrapper.map.ConcurrentBusyWaitingIntLongMap; +import com.trivago.fastutilconcurrentwrapper.map.ConcurrentIntLongMap; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ConcurrentIntLongMapBuilderTest { + private final long DEFAULT_VALUE = -1L; + + @Test + public void simpleBuilderTest() { + ConcurrentIntLongMapBuilder b = ConcurrentIntLongMapBuilder.newBuilder() + .withBuckets(2) + .withDefaultValue(DEFAULT_VALUE) + .withInitialCapacity(100) + .withMode(ConcurrentIntLongMapBuilder.MapMode.BUSY_WAITING) + .withLoadFactor(0.9f); + + IntLongMap map = b.build(); + + map.put(1, 10L); + long v = map.get(1); + + assertInstanceOf(ConcurrentBusyWaitingIntLongMap.class, map); + assertEquals(10L, v); + assertEquals(map.get(2), map.getDefaultValue()); + } + + @Test + public void buildsBlockingMap() { + ConcurrentIntLongMapBuilder b = ConcurrentIntLongMapBuilder.newBuilder() + .withBuckets(2) + .withDefaultValue(DEFAULT_VALUE) + .withInitialCapacity(100) + .withMode(ConcurrentIntLongMapBuilder.MapMode.BLOCKING) + .withLoadFactor(0.9f); + + IntLongMap map = b.build(); + + map.put(1, 10L); + long v = map.get(1); + + assertInstanceOf(ConcurrentIntLongMap.class, map); + assertEquals(10L, v); + assertEquals(map.get(2), map.getDefaultValue()); + } +} diff --git a/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/AbstractIntLongMapTest.java b/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/AbstractIntLongMapTest.java new file mode 100644 index 0000000..7d5eb35 --- /dev/null +++ b/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/AbstractIntLongMapTest.java @@ -0,0 +1,206 @@ +package com.trivago.fastutilconcurrentwrapper.intlong; + +import com.trivago.fastutilconcurrentwrapper.AbstractMapTest; +import com.trivago.fastutilconcurrentwrapper.IntLongMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +abstract class AbstractIntLongMapTest extends AbstractMapTest { + private IntLongMap map; + // Keep the default value to easily verify that this value is returned. + protected long defaultValue; + // Some methods return the default value of the underlying Fastutil implementation. + private static final long FASTUTIL_DEFAULT_VALUE = 0L; + + abstract IntLongMap createMap(); + + @BeforeEach + void initializeMap() { + defaultValue = nextLong(); + map = createMap(); + } + + @Test + protected void containsKeyReturnsFalseIfMapIsEmpty() { + final int key = nextInt(); + + final boolean contains = map.containsKey(key); + + assertFalse(contains); + } + + @Test + protected void containsKeyReturnsTrueIfKeyExists() { + int key = nextInt(); + long value = nextLong(); + map.put(key, value); + + final boolean contains = map.containsKey(key); + + assertTrue(contains); + } + + @Test + protected void containsKeyReturnsFalseIfKeyWasRemoved() { + int key = nextInt(); + long value = nextLong(); + map.put(key, value); + map.remove(key); + + final boolean contains = map.containsKey(key); + + assertFalse(contains); + } + + @Test + protected void mapIsEmptyWhenNothingWasInserted() { + final boolean empty = map.isEmpty(); + + assertTrue(empty); + } + + @Test + protected void mapIsEmptyWhenAllKeysAreDeleted() { + int entryCount = (Math.abs(nextInt()) % 100) + 1; + long value = nextLong(); + + for (int key = 1; key <= entryCount; key++) { + map.put(key, value); + } + for (int key = 1; key <= entryCount; key++) { + map.remove(key); + } + + final boolean empty = map.isEmpty(); + + assertTrue(empty); + } + + @Test + protected void sizeIsCorrect() { + int entries = (Math.abs(nextInt()) % 50) + 1; + long value = nextLong(); + + for (int key = 1; key <= entries; key++) { + map.put(key, value); + } + + final int size = map.size(); + + assertEquals(entries, size); + } + + @Test + protected void gettingExistingValueReturnsCorrectValue() { + int key = nextInt(); + long value = nextLong(); + map.put(key, value); + final long returnedValue = map.get(key); + + assertEquals(value, returnedValue); + } + + @Test + protected void gettingNonExistingValueReturnsCorrectValue() { + int key = nextInt(); + final long returnedValue = map.get(key); + + assertEquals(defaultValue, returnedValue); + } + + @Test + protected void removingNonExistingKeyReturnsDefaultValue() { + int key = nextInt(); + final long removedValue = map.remove(key); + + assertEquals(FASTUTIL_DEFAULT_VALUE, removedValue); + } + + @Test + protected void removingExistingKeyReturnsPreviousValue() { + int key = nextInt(); + long value = nextLong(); + map.put(key, value); + final long removedValue = map.remove(key); + + assertEquals(value, removedValue); + } + + @Test + protected void removingWithValueWhenKeyDoesNotExistReturnsFalse() { + int key = nextInt(); + long value = nextLong(); + final boolean result = map.remove(key, value); + + assertFalse(result); + } + + @Test + protected void removingWithValueWhenValueIsDifferentReturnsFalse() { + int key = nextInt(); + long value = nextLong(); + map.put(key, value); + final boolean result = map.remove(key, value - 1); + + assertFalse(result); + } + + @Test + protected void removingWithValueWhenValueIsSameReturnsTrue() { + int key = nextInt(); + long value = nextLong(); + map.put(key, value); + final boolean result = map.remove(key, value); + + assertTrue(result); + } + + @Test + protected void puttingValueIfAbsentReturnsSameValue() { + int key = nextInt(); + long value = nextLong(); + map.computeIfAbsent(key, l -> value); + + long result = map.get(key); + + assertEquals(result, value); + } + + @Test + protected void checkingValueIfNotAbsentReturnsSameValue() { + int key = nextInt(); + long value = nextLong(); + map.put(key, value); + long returned = map.computeIfAbsent(key, l -> value); + + long result = map.get(key); + + assertEquals(result, value); + assertEquals(value, returned); + } + + @Test + protected void replacingValueIfPresentReturnsNewValue() { + int key = nextInt(); + long value = nextLong(); + map.put(key, value); + + map.computeIfPresent(key, Long::sum); // key + old value + + long result = map.get(key); + + assertEquals(result, key + value); + } + + @Test + protected void checkingValueIfNotPresentReturnsDefaultValue() { + int key = nextInt(); + map.computeIfPresent(key, Long::sum); + + long result = map.get(key); + + assertEquals(result, map.getDefaultValue()); + } +} diff --git a/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/ConcurrentBusyWaitingIntLongMapTest.java b/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/ConcurrentBusyWaitingIntLongMapTest.java new file mode 100644 index 0000000..390a6b5 --- /dev/null +++ b/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/ConcurrentBusyWaitingIntLongMapTest.java @@ -0,0 +1,12 @@ +package com.trivago.fastutilconcurrentwrapper.intlong; + +import com.trivago.fastutilconcurrentwrapper.IntLongMap; +import com.trivago.fastutilconcurrentwrapper.map.ConcurrentBusyWaitingIntLongMap; + +public class ConcurrentBusyWaitingIntLongMapTest extends AbstractIntLongMapTest { + + @Override + IntLongMap createMap() { + return new ConcurrentBusyWaitingIntLongMap(16, 16, 0.9F, defaultValue); + } +} diff --git a/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/ConcurrentIntLongMapTest.java b/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/ConcurrentIntLongMapTest.java new file mode 100644 index 0000000..aebc8c1 --- /dev/null +++ b/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/ConcurrentIntLongMapTest.java @@ -0,0 +1,12 @@ +package com.trivago.fastutilconcurrentwrapper.intlong; + +import com.trivago.fastutilconcurrentwrapper.IntLongMap; +import com.trivago.fastutilconcurrentwrapper.map.ConcurrentIntLongMap; + +public class ConcurrentIntLongMapTest extends AbstractIntLongMapTest { + + @Override + IntLongMap createMap() { + return new ConcurrentIntLongMap(16, 16, 0.9F, defaultValue); + } +} diff --git a/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/PrimitiveFastutilIntLongWrapperTest.java b/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/PrimitiveFastutilIntLongWrapperTest.java new file mode 100644 index 0000000..8814b1f --- /dev/null +++ b/src/test/java/com/trivago/fastutilconcurrentwrapper/intlong/PrimitiveFastutilIntLongWrapperTest.java @@ -0,0 +1,12 @@ +package com.trivago.fastutilconcurrentwrapper.intlong; + +import com.trivago.fastutilconcurrentwrapper.IntLongMap; +import com.trivago.fastutilconcurrentwrapper.wrapper.PrimitiveFastutilIntLongWrapper; + +public class PrimitiveFastutilIntLongWrapperTest extends AbstractIntLongMapTest { + + @Override + IntLongMap createMap() { + return new PrimitiveFastutilIntLongWrapper(5, 0.9F, defaultValue); + } +}