diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..3c76a92
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
new file mode 100644
index 0000000..231fa24
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ long-map
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..1f887b6
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..cb635b1
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java
index 2f0b54b..a1d318a 100644
--- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java
+++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java
@@ -1,43 +1,261 @@
package de.comparus.opensource.longmap;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
public class LongMapImpl implements LongMap {
- public V put(long key, V value) {
- return null;
- }
- public V get(long key) {
- return null;
- }
+ private static final int DEFAULT_CAPACITY = 16;
+ private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ private int capacity;
+ private float loadFactor;
+ private int size;
+ private Entry[] buckets;
+ long[] keysCache = null;
+ V[] valuesCache = null;
+
+ Class type = (Class) Object.class;
+
+ public LongMapImpl() {
+ this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, null);
+ }
+
+ public LongMapImpl(Class type) {
+ this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, type);
+ }
+
+ public LongMapImpl(int capacity, float loadFactor, Class type) {
+ if (capacity <= 0) {
+ throw new IllegalArgumentException(
+ "Capacity can not be less zero." + System.lineSeparator() + "Invalid capacity: " + capacity);
+ }
+ if (loadFactor <= 0 || loadFactor > 1) {
+ throw new IllegalArgumentException("The value of the load factor can only be in the range of 0 to 1"
+ + System.lineSeparator() + "Invalid load factor: " + loadFactor);
+ }
+
+ this.type = type;
+ this.capacity = capacity;
+ this.loadFactor = loadFactor;
+ this.size = 0;
+ this.buckets = new Entry[capacity];
+ }
+
+ public V put(long key, V value) {
+ keysCache = null;
+ valuesCache = null;
+ if (type == null || type == Object.class) {
+ type = (Class) value.getClass();
+ } else if (value.getClass() != type) {
+ throw new IllegalArgumentException(
+ "Invalid value type. Available only " + type.getName() + "values for inserting");
+ }
+ int bucketIndex = getBucketIndex(key, capacity);
+ Entry bucketEntry = buckets[bucketIndex];
+ if (bucketEntry != null) {
+ Entry lastNotNullEntry = null;
+ while (bucketEntry != null) {
+ if (bucketEntry.getKey() == key) {
+ V prevValue = bucketEntry.getValue();
+ lastNotNullEntry.next = bucketEntry;
+ bucketEntry.setValue(value);
+ return prevValue;
+ }
+ lastNotNullEntry = bucketEntry;
+ bucketEntry = bucketEntry.next;
+
+ }
+ lastNotNullEntry.next = new Entry(key, value);
+
+ } else {
+ buckets[bucketIndex] = new Entry(key, value);
+ }
+ size++;
+
+ if (size > capacity * loadFactor) {
+ resize();
+ }
+ return null;
+ }
+
+ public V get(long key) {
+ int bucketIndex = getBucketIndex(key, capacity);
+ Entry bucketEntry = buckets[bucketIndex];
+ if (bucketEntry == null) {
+ return null;
+ } else if (bucketEntry.getKey() == key) {
+ return bucketEntry.getValue();
+ } else {
+ Entry prevEntry = null;
+ while (bucketEntry != null) {
+ prevEntry = bucketEntry;
+ bucketEntry = prevEntry.next;
+ if (bucketEntry.getKey() == key) {
+ return bucketEntry.getValue();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public V remove(long key) {
+ keysCache = null;
+ valuesCache = null;
+ int bucketIndex = getBucketIndex(key, capacity);
+ Entry bucketEntry = buckets[bucketIndex];
+ if (bucketEntry == null) {
+ return null;
+ } else if (bucketEntry.getKey() == key) {
+ V value = bucketEntry.getValue();
+ buckets[bucketIndex] = null;
+ size--;
+ return value;
+ } else {
+ Entry prevEntry = null;
+ while (bucketEntry != null) {
+ prevEntry = bucketEntry;
+ bucketEntry = prevEntry.next;
+ if (bucketEntry.getKey() == key) {
+ Entry nextEntry = bucketEntry.next;
+ prevEntry.next = nextEntry;
+ size--;
+ return bucketEntry.getValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ public boolean containsKey(long key) {
+ int bucketIndex = getBucketIndex(key, capacity);
+ Entry bucketEntry = buckets[bucketIndex];
+ if (bucketEntry != null) {
+ while (bucketEntry != null) {
+ if (bucketEntry.getKey() == key) {
+ return true;
+ }
+ bucketEntry = bucketEntry.next;
+ }
+ }
+ return false;
+ }
+
+ public boolean containsValue(V value) {
+ V[] values = values();
+ return Arrays.asList(values).contains(value);
+ }
+
+ public long[] keys() {
+ if (keysCache != null) {
+ return keysCache;
+ }
+ Set keySet = new HashSet();
+ for (Entry entry : buckets) {
+ if (entry != null) {
+ while (entry != null) {
+ keySet.add(entry.getKey());
+ entry = entry.next;
+
+ }
+ }
+ }
+
+ long[] keys = new long[keySet.size()];
+ int i = 0;
+ for (long key : keySet) {
+ keys[i++] = key;
+ }
+ keysCache = keys;
+ return keys;
+ }
+
+ @SuppressWarnings("unchecked")
+ public V[] values() {
+ if (valuesCache != null) {
+ return valuesCache;
+ }
+ int i = 0;
+ V[] values = (V[]) Array.newInstance(type, size);
+ for (Entry entry : buckets) {
+ if (entry != null) {
+ while (entry != null) {
+ values[i] = entry.getValue();
+ i++;
+ entry = entry.next;
+
+ }
+ }
+ }
+ valuesCache = values;
+ return values;
+ }
+
+ public long size() {
+ return size;
+ }
+
+ public void clear() {
+ size = 0;
+ buckets = new Entry[capacity];
+ }
+
+ private static class Entry {
+ private long key;
+ private V value;
- public V remove(long key) {
- return null;
- }
+ private Entry next;
- public boolean isEmpty() {
- return false;
- }
+ public Entry(long key, V value) {
+ this.key = key;
+ this.value = value;
+ }
- public boolean containsKey(long key) {
- return false;
- }
+ public long getKey() {
+ return key;
+ }
- public boolean containsValue(V value) {
- return false;
- }
+ public V getValue() {
+ return value;
+ }
- public long[] keys() {
- return null;
- }
+ public void setValue(V value) {
+ this.value = value;
+ }
+ }
- public V[] values() {
- return null;
- }
+ private void resize() {
+ int newCapacity = capacity + capacity / 2;
+ if (newCapacity < 0) {
+ newCapacity = Integer.MAX_VALUE;
+ }
+ Entry[] newBuckets = new Entry[newCapacity];
- public long size() {
- return 0;
- }
+ for (Entry bucket : buckets) {
+ while (bucket != null) {
+ int newBucketIndex = getBucketIndex(bucket.getKey(), newCapacity);
+ Entry newBucket = newBuckets[newBucketIndex];
+ if (newBucket == null) {
+ newBuckets[newBucketIndex] = bucket;
+ } else {
+ newBucket.next = bucket;
+ }
+ bucket = bucket.next;
+ }
+ }
- public void clear() {
+ capacity = newCapacity;
+ buckets = newBuckets;
+ }
- }
+ private int getBucketIndex(long key, int capacity) {
+ return ((int) Math.abs(key) % capacity);
+ }
}
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..059c723
--- /dev/null
+++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java
@@ -0,0 +1,145 @@
+package de.comparus.opensource.longmap;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class LongMapImplTest {
+
+ private LongMapImpl longMapImpl = new LongMapImpl();
+
+ @BeforeEach
+ void fillMap() {
+ int i = -500_000;
+ while (i < 500_000) {
+ longMapImpl.put(i, i);
+ i++;
+ }
+ }
+
+ @Test
+ void testPut() {
+ int i = -500_000;
+ boolean isAllPutted = true;
+ while (i < 500_000) {
+ if (longMapImpl.get(i) == null) {
+ isAllPutted = false;
+ break;
+ }
+ i++;
+ }
+ assertTrue(isAllPutted);
+ }
+
+ @Test
+ void testGet() {
+ int j = 0;
+ int i = -500_000;
+ while (j < 1_000_000) {
+ longMapImpl.get(i);
+ j++;
+ i++;
+ }
+ assertEquals(longMapImpl.size(), j);
+ }
+
+ @Test
+ void testRemove() {
+ int i = -500_000;
+ boolean isAllremoved = true;
+ while (i < 1_000_000) {
+ longMapImpl.remove(i);
+ if (longMapImpl.get(i) != null) {
+ isAllremoved = false;
+ break;
+ }
+ i++;
+ }
+ assertTrue(isAllremoved);
+ }
+
+ @Test
+ void testIsEmpty() {
+
+ assertFalse(longMapImpl.isEmpty());
+ longMapImpl.clear();
+ assertTrue(longMapImpl.isEmpty());
+ }
+
+ @Test
+ void testContainsKey() {
+ int i = -500_000;
+ boolean isContains = true;
+ while (i < 500_000) {
+ isContains = longMapImpl.containsKey(i);
+ if (!isContains) {
+ break;
+ }
+ i++;
+ }
+ assertTrue(isContains);
+ }
+
+ @Test
+ void testContainsValue() {
+ int i = -5000;
+ boolean isContains = true;
+ while (i < 5000) {
+ isContains = longMapImpl.containsValue(i);
+ if (!isContains) {
+ break;
+ }
+ i++;
+ }
+ assertTrue(isContains);
+ }
+
+ @Test
+ void testKeys() {
+ long[] expectedKeys = new long[1_000_000];
+ int i = -500_000;
+ int j = 0;
+ while (j < expectedKeys.length) {
+ expectedKeys[j] = i;
+ i++;
+ j++;
+ }
+ long[] actualKeys = longMapImpl.keys();
+ assertEquals(expectedKeys.length, actualKeys.length);
+ }
+
+ @Test
+ void testValues() {
+ Integer[] expectedValues = new Integer[1000000];
+ int i = -500_000;
+ int j = 0;
+ while (j < expectedValues.length) {
+ expectedValues[j] = i;
+ i++;
+ j++;
+ }
+
+ assertEquals(expectedValues.length, longMapImpl.values().length);
+ }
+
+ @Test
+ void testSize() {
+ int i = -500_000;
+ int j = 0;
+ while (j < 10) {
+ longMapImpl.remove(i);
+ i++;
+ j++;
+ }
+ assertTrue(longMapImpl.size() == longMapImpl.values().length);
+ }
+
+ @Test
+ void testClear() {
+ longMapImpl.clear();
+ assertTrue(longMapImpl.isEmpty());
+ assertTrue(longMapImpl.size() == 0);
+ }
+
+}