LongLongHashMap.get should no longer throw NoSuchElementException

In many use cases it is more efficient to have a getter that returns
a default value instead of throwing an exception. The exception forces
every consumer to call containsKey() before calling get(). In cases
where the consumer wants to use a default value this is unnecessary.
This commit is contained in:
2019-12-22 19:14:41 +01:00
parent 955b410ba3
commit ec7a03f068
2 changed files with 18 additions and 19 deletions

View File

@@ -1,10 +1,11 @@
package org.lucares.collections; package org.lucares.collections;
import java.util.Arrays; import java.util.Arrays;
import java.util.NoSuchElementException;
/** /**
* A hash map where key and value are primitive longs. * A hash map where key and value are primitive longs.
*
* @see LongObjHashMap
*/ */
public class LongLongHashMap { public class LongLongHashMap {
@@ -125,21 +126,19 @@ public class LongLongHashMap {
} }
/** /**
* Returns the value for the given key if it exists. This method throws a * Returns the value for the given key if it exists or {@code defaultValue} if it does not exist.
* {@link NoSuchElementException} if the key does not exist. Use
* {@link #containsKey(long)} to check before calling {@link #get(long)}.
* *
* @param key the key * @param key the key
* @return the value if it exists * @param defaultValue the value to return if the given key does not exist
* @throws NoSuchElementException if the value does not exist * @return the value if it exists, or defaultValue
*/ */
public long get(final long key) { public long get(final long key, long defaultValue) {
if (key == NULL_KEY) { if (key == NULL_KEY) {
if (zeroValue != null) { if (zeroValue != null) {
return zeroValue; return zeroValue;
} }
throw new NoSuchElementException(); return defaultValue;
} }
final int searchStart = spread(key); final int searchStart = spread(key);
@@ -150,7 +149,7 @@ public class LongLongHashMap {
} }
currentPosition = (currentPosition + 1) % keys.length; currentPosition = (currentPosition + 1) % keys.length;
} while (currentPosition != searchStart); } while (currentPosition != searchStart);
throw new NoSuchElementException(); return defaultValue;
} }
/** /**
@@ -206,7 +205,7 @@ public class LongLongHashMap {
* <p> * <p>
* The mapping for given key is updated by calling {@code function} with the old * The mapping for given key is updated by calling {@code function} with the old
* value. The return value will be set as new value. If the map does not contain * value. The return value will be set as new value. If the map does not contain
* a mapping for the key, then {@code function} is with * a mapping for the key, then {@code function} is called with
* {@code initialValueIfAbsent}. * {@code initialValueIfAbsent}.
* *
* @param key the key * @param key the key
@@ -280,7 +279,7 @@ public class LongLongHashMap {
for (int i = 0; i < sortedKeys.length; i++) { for (int i = 0; i < sortedKeys.length; i++) {
final long key = sortedKeys[i]; final long key = sortedKeys[i];
if (key != EMPTY_SLOT) { if (key != EMPTY_SLOT) {
consumer.accept(key, get(key)); consumer.accept(key, get(key, 0)); // the default value of 'get' will not be used, because the key exists
} else if (key == EMPTY_SLOT) { } else if (key == EMPTY_SLOT) {
final int posFirstKey = findPosOfFirstPositiveKey(sortedKeys); final int posFirstKey = findPosOfFirstPositiveKey(sortedKeys);
if (posFirstKey < 0) { if (posFirstKey < 0) {

View File

@@ -30,12 +30,12 @@ public class LongLongHashMapTest {
// add value and check it is in the map // add value and check it is in the map
map.put(key, valueA); map.put(key, valueA);
Assertions.assertTrue(map.containsKey(key)); Assertions.assertTrue(map.containsKey(key));
Assertions.assertEquals(valueA, map.get(key)); Assertions.assertEquals(valueA, map.get(key, Long.MIN_VALUE));
Assertions.assertEquals(1, map.size()); Assertions.assertEquals(1, map.size());
// overwrite value // overwrite value
map.put(key, valueB); map.put(key, valueB);
Assertions.assertEquals(valueB, map.get(key)); Assertions.assertEquals(valueB, map.get(key, Long.MIN_VALUE));
Assertions.assertEquals(1, map.size()); Assertions.assertEquals(1, map.size());
// remove value and check it is gone // remove value and check it is gone
@@ -50,10 +50,10 @@ public class LongLongHashMapTest {
final long key = 1; final long key = 1;
map.compute(key, 6, l -> l + 1); map.compute(key, 6, l -> l + 1);
Assertions.assertEquals(7, map.get(key), "initialValueIfAbsent is used when there is no mapping for the key"); Assertions.assertEquals(7, map.get(key, Long.MIN_VALUE), "initialValueIfAbsent is used when there is no mapping for the key");
map.compute(key, 6, l -> l + 1); map.compute(key, 6, l -> l + 1);
Assertions.assertEquals(8, map.get(key), "update function is called when 'zeroKey' is set"); Assertions.assertEquals(8, map.get(key, Long.MIN_VALUE), "update function is called when 'zeroKey' is set");
} }
@Test @Test
@@ -61,10 +61,10 @@ public class LongLongHashMapTest {
final LongLongHashMap map = new LongLongHashMap(); final LongLongHashMap map = new LongLongHashMap();
final long key = 1; final long key = 1;
map.compute(key, 6, l -> l + 1); map.compute(key, 6, l -> l + 1);
Assertions.assertEquals(7, map.get(key), "initialValueIfAbsent is used when there is no mapping for the key"); Assertions.assertEquals(7, map.get(key, Long.MIN_VALUE), "initialValueIfAbsent is used when there is no mapping for the key");
map.compute(key, 6, l -> l + 1); map.compute(key, 6, l -> l + 1);
Assertions.assertEquals(8, map.get(key), "update function is called when key is set"); Assertions.assertEquals(8, map.get(key, Long.MIN_VALUE), "update function is called when key is set");
} }
@Test @Test
@@ -79,7 +79,7 @@ public class LongLongHashMapTest {
map.put(l, l); map.put(l, l);
}); });
entries.stream().forEachOrdered(l -> { entries.stream().forEachOrdered(l -> {
Assertions.assertEquals(l, map.get(l)); Assertions.assertEquals(l, map.get(l, Long.MIN_VALUE));
}); });
Assertions.assertEquals(16, map.getCapacity(), "capacity after adding 12 entries must be a the smallest number " Assertions.assertEquals(16, map.getCapacity(), "capacity after adding 12 entries must be a the smallest number "
+ "that satisfies initialCapacity * 2^n >= entries/fillFactor"); + "that satisfies initialCapacity * 2^n >= entries/fillFactor");
@@ -95,7 +95,7 @@ public class LongLongHashMapTest {
keysWithSameSpread.stream().forEach(l -> map.put(l, l)); keysWithSameSpread.stream().forEach(l -> map.put(l, l));
Assertions.assertEquals(keysWithSameSpread.size(), map.size()); Assertions.assertEquals(keysWithSameSpread.size(), map.size());
keysWithSameSpread.stream().forEach(l -> Assertions.assertEquals(l, map.get(l))); keysWithSameSpread.stream().forEach(l -> Assertions.assertEquals(l, map.get(l, Long.MIN_VALUE)));
} }
@Test @Test