diff --git a/pom.xml b/pom.xml
index 36c092b..0373da4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,11 +26,24 @@
+
+ org.apache.commons
+ commons-lang3
+ 3.13.0
+
+
junitjunit
- 4.12
+ 4.13.2test
+
+
+ org.projectlombok
+ lombok
+ 1.18.28
+ provided
+
diff --git a/src/main/java/de/comparus/opensource/longmap/LongMap.java b/src/main/java/de/comparus/opensource/longmap/LongMap.java
index adbf242..11c2ab1 100644
--- a/src/main/java/de/comparus/opensource/longmap/LongMap.java
+++ b/src/main/java/de/comparus/opensource/longmap/LongMap.java
@@ -1,17 +1,82 @@
package de.comparus.opensource.longmap;
+/**
+ * A map with keys of type long.
+ *
+ * @param the type of values stored in the map
+ */
public interface LongMap {
+ /**
+ * Associates the specified value with the specified key in this map.
+ *
+ * @param key the key with which the specified value is to be associated
+ * @param value the value to be associated with the specified key
+ * @return the previous value associated with the key, or null if there was no mapping for the key
+ */
V put(long key, V value);
+
+ /**
+ * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
+ *
+ * @param key the key whose associated value is to be returned
+ * @return the value to which the specified key is mapped, or null if this map contains no mapping for the key
+ */
V get(long key);
+
+ /**
+ * Removes the mapping for the specified key from this map if present.
+ *
+ * @param key the key whose mapping is to be removed from the map
+ * @return the previous value associated with the key, or null if there was no mapping for the key
+ */
V remove(long key);
+ /**
+ * Returns true if this map contains no key-value mappings.
+ *
+ * @return true if this map contains no key-value mappings
+ */
boolean isEmpty();
+
+ /**
+ * Returns true if this map contains a mapping for the specified key.
+ *
+ * @param key the key whose presence in this map is to be tested
+ * @return true if this map contains a mapping for the specified key
+ */
boolean containsKey(long key);
+
+ /**
+ * Returns true if this map contains at least one mapping with the specified value.
+ *
+ * @param value the value whose presence in this map is to be tested
+ * @return true if this map contains at least one mapping with the specified value
+ */
boolean containsValue(V value);
+ /**
+ * Returns an array containing all the keys in this map.
+ *
+ * @return an array containing all the keys in this map
+ */
long[] keys();
+
+ /**
+ * Returns an array containing all the values in this map.
+ *
+ * @return an array containing all the values in this map
+ */
V[] values();
+ /**
+ * Returns the number of key-value mappings in this map.
+ *
+ * @return the number of key-value mappings in this map
+ */
long size();
+
+ /**
+ * Removes all of the mappings from this map. The map will be empty after this call returns.
+ */
void clear();
}
diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java
index 2f0b54b..4b60d71 100644
--- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java
+++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java
@@ -1,43 +1,183 @@
package de.comparus.opensource.longmap;
+import lombok.AllArgsConstructor;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
public class LongMapImpl implements LongMap {
+
+ private static final int CAPACITY_LEVEL = 2;
+ private static final int DEFAULT_CAPACITY = 16;
+ private static final double LOAD_FACTOR = 0.75;
+
+ private Entry[] table;
+ private int size;
+
+ public LongMapImpl() {
+ this(DEFAULT_CAPACITY);
+ }
+
+ public LongMapImpl(int initialCapacity) {
+ if (initialCapacity <= 0) {
+ throw new IllegalArgumentException("Initial capacity must be positive");
+ }
+ table = new Entry[initialCapacity];
+ size = 0;
+ }
+
+ @Override
public V put(long key, V value) {
- return null;
+ if (size >= table.length * LOAD_FACTOR) {
+ resize();
+ }
+ int index = hash(key) % table.length;
+
+ Entry entry = table[index];
+ while (entry != null) {
+ if (entry.key == key) {
+ entry.value = value;
+ return value;
+ }
+ entry = entry.next;
+ }
+
+ table[index] = new Entry<>(key, value, table[index]);
+ size++;
+ return value;
}
+ @Override
public V get(long key) {
+ int index = hash(key) % table.length;
+ Entry entry = table[index];
+ while (entry != null) {
+ if (entry.key == key) {
+ return entry.value;
+ }
+ entry = entry.next;
+ }
return null;
}
+ @Override
public V remove(long key) {
- return null;
+ int index = hash(key) % table.length;
+ Entry prev = null;
+ Entry current = table[index];
+
+ while (current != null) {
+ if (current.key == key) {
+ if (prev == null) {
+ table[index] = current.next;
+ } else {
+ prev.next = current.next;
+ }
+ size--;
+ return current.value;
+ }
+ prev = current;
+ current = current.next;
+ }
+ return prev != null ? prev.value : null;
}
+ @Override
public boolean isEmpty() {
- return false;
+ return size == 0;
}
+ @Override
public boolean containsKey(long key) {
+ int index = hash(key) % table.length;
+ Entry entry = table[index];
+ while (entry != null) {
+ if (entry.key == key) {
+ return true;
+ }
+ entry = entry.next;
+ }
return false;
}
+ @Override
public boolean containsValue(V value) {
+ for (Entry entry : table) {
+ while (entry != null) {
+ if (Objects.equals(entry.value, value)) {
+ return true;
+ }
+ entry = entry.next;
+ }
+ }
return false;
}
+ @Override
public long[] keys() {
- return null;
+ long[] keys = new long[(int) size()];
+ int index = 0;
+ for (Entry entry : table) {
+ while (entry != null) {
+ keys[index++] = entry.key;
+ entry = entry.next;
+ }
+ }
+ return keys;
}
+ @Override
public V[] values() {
- return null;
+ List valueList = new ArrayList<>();
+ for (Entry entry : table) {
+ while (entry != null) {
+ valueList.add(entry.value);
+ entry = entry.next;
+ }
+ }
+ return valueList.toArray((V[]) Array.newInstance(valueList.get(0).getClass(), valueList.size()));
}
+ @Override
public long size() {
- return 0;
+ return size;
}
+ @Override
public void clear() {
+ if ((table) != null && size > 0) {
+ size = 0;
+ Arrays.fill(table, null);
+ }
+ }
+
+ private int hash(long key) {
+ return Long.hashCode(key);
+ }
+
+ private void resize() {
+ int newCapacity = table.length * CAPACITY_LEVEL;
+ Entry[] newTable = new Entry[newCapacity];
+
+ for (Entry entry : table) {
+ while (entry != null) {
+ int index = hash(entry.key) % newCapacity;
+ Entry next = entry.next;
+ entry.next = newTable[index];
+ newTable[index] = entry;
+ entry = next;
+ }
+ }
+ table = newTable;
+ }
+ @AllArgsConstructor
+ private static class Entry {
+ long key;
+ V value;
+ Entry next;
}
}
diff --git a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java
new file mode 100644
index 0000000..ae52a57
--- /dev/null
+++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java
@@ -0,0 +1,224 @@
+package de.comparus.opensource.longmap;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.LongStream;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author deya
+ */
+@RunWith(DataProviderRunner.class)
+public class LongMapImplTest {
+ private static final int NUM_OPERATIONS = 25;
+ private final LongMap map = new LongMapImpl<>();
+
+ @Before
+ public void setUp() {
+ map.clear();
+ }
+
+ @Test
+ @UseDataProvider("testGenericDataProvider")
+ public void testPutAndGet(Map initialMap) {
+ for (Map.Entry entry : initialMap.entrySet()) {
+ String putResult = map.put(entry.getKey(), entry.getValue());
+ assertNotNull(putResult);
+ assertEquals(entry.getValue(), putResult);
+
+ String getResult = map.get(entry.getKey());
+ assertNotNull(getResult);
+ assertEquals(entry.getValue(), getResult);
+ }
+
+ assertNull(map.get(generateKey()));
+ assertNull(null);
+ }
+
+ @Test
+ public void testPutAndGetObject() {
+ LongMap