diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 2f0b54b..e0a7360 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -1,43 +1,220 @@ package de.comparus.opensource.longmap; +import java.util.Arrays; + public class LongMapImpl implements LongMap { + + private static final int DEFAULT_CAPACITY = 16; + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + private static final int DEFAULT_INCREASE_CAPACITY = 2; + + private int size; + private int capacity; + private int threshold; + private Node[] table; + + private static class Node { + + private final long key; + private V value; + private Node next; + + public Node(long key, V value) { + this.key = key; + this.value = value; + } + + public boolean hasNext() { + return next != null; + } + } + + public LongMapImpl() { + this.capacity = DEFAULT_CAPACITY; + this.table = new Node[DEFAULT_CAPACITY]; + this.threshold = (int) (capacity * DEFAULT_LOAD_FACTOR); + } + public V put(long key, V value) { - return null; + if (threshold < size) { + resize(); + } + int index = indexCalculation(key); + + if (table[index] == null) { + table[index] = new Node(key, value); + size++; + + return value; + } + + Node currentNode = table[index]; + while (currentNode.hasNext() || currentNode.key == key) { + if (currentNode.key == key) { + currentNode.value = value; + + return value; + } + + currentNode = currentNode.next; + } + currentNode.next = new Node<>(key, value); + size++; + + return value; } public V get(long key) { + if (isEmpty()) { + return null; + } + int index = indexCalculation(key); + Node currentNode = table[index]; + + while (currentNode != null) { + if (currentNode.key == key) { + return currentNode.value; + } + + currentNode = currentNode.next; + } + return null; } public V remove(long key) { + int index = indexCalculation(key); + if (isEmpty() || table[index] == null) { + return null; + } + Node currentNode = table[index]; + + if (currentNode.key == key) { + table[index] = currentNode.next; + size--; + + return currentNode.value; + } + + while (currentNode.hasNext()) { + if (currentNode.next.key == key) { + V removed = currentNode.next.value; + currentNode.next = currentNode.next.next; + size--; + + return removed; + } + + currentNode = currentNode.next; + } + return null; } public boolean isEmpty() { - return false; + return size == 0; } public boolean containsKey(long key) { + if (isEmpty()) { + return false; + } + int index = indexCalculation(key); + Node currentNode = table[index]; + + while (currentNode != null) { + if (currentNode.key == key) { + return true; + } + + currentNode = currentNode.next; + } + return false; } public boolean containsValue(V value) { + if (isEmpty()) { + return false; + } + + for (Node currentNode : table) { + while (currentNode != null) { + if (currentNode.value != null && currentNode.value.equals(value)) { + return true; + } + + currentNode = currentNode.next; + } + } + return false; } public long[] keys() { - return null; + if (isEmpty()) { + return new long[0]; + } + long[] keys = new long[size]; + int index = 0; + + for (Node currentNode : table) { + while (currentNode != null) { + keys[index++] = currentNode.key; + + currentNode = currentNode.next; + } + } + + return keys; } public V[] values() { - return null; + if (isEmpty()) { + return (V[]) new Object[0]; + } + V[] values = (V[]) new Object[size]; + int index = 0; + + for (Node currentNode : table) { + while (currentNode != null) { + values[index++] = currentNode.value; + + currentNode = currentNode.next; + } + } + + return values; } public long size() { - return 0; + return size; } public void clear() { + if (table != null && size > 0) { + size = 0; + Arrays.fill(table, null); + } + } + + private int indexCalculation(long key) { + return Long.hashCode(key) & (capacity - 1); + } + + private void resize() { + Node[] oldTable = table; + table = new Node[table.length * DEFAULT_INCREASE_CAPACITY]; + capacity = table.length; + size = 0; + threshold = (int) (oldTable.length * DEFAULT_LOAD_FACTOR); + + for (Node currantNode : oldTable) { + while (currantNode != null) { + put(currantNode.key, currantNode.value); + currantNode = currantNode.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..306379a --- /dev/null +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -0,0 +1,178 @@ +package de.comparus.opensource.longmap; + +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class LongMapImplTest { + + @Test + public void put_positiveScenario_shouldSuccess() { + LongMap map = new LongMapImpl<>(); + + assertEquals("First", map.put(1, "First")); + assertEquals("Second", map.put(2, "Second")); + } + + @Test + public void get_positiveScenario_shouldSuccess() { + LongMap map = new LongMapImpl<>(); + map.put(1, "First"); + map.put(2, "Second"); + + assertEquals("First", map.get(1)); + assertEquals("Second", map.get(2)); + } + + @Test + public void get_fromEmptyMap_shouldReturnNull() { + LongMap map = new LongMapImpl<>(); + + assertNull(map.get(1)); + } + + @Test + public void get_byNotExistingKey_shouldReturnNull() { + LongMap map = new LongMapImpl<>(); + map.put(1, "First"); + map.put(2, "Second"); + + assertNull(map.get(3)); + } + + @Test + public void remove_positiveScenario_shouldSuccess() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertEquals("Zero", map.remove(0)); + assertEquals("First", map.remove(1)); + } + + @Test + public void remove_fromEmptyMap_shouldReturnNull() { + LongMap map = new LongMapImpl<>(); + + assertNull(map.remove(0)); + } + + @Test + public void remove_byNotExistingKey_shouldReturnNull() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertNull(map.get(3)); + } + + @Test + public void isEmpty_positiveScenario_shouldReturnFalse() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertFalse(map.isEmpty()); + } + + @Test + public void isEmpty_fromEmptyMap_shouldReturnTrue() { + LongMap map = new LongMapImpl<>(); + + assertTrue(map.isEmpty()); + } + + @Test + public void containsKey_positiveScenario_shouldReturnTrue() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertTrue(map.containsKey(0)); + } + + @Test + public void containsKey_byNotExistingKey_shouldReturnFalse() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertFalse(map.containsKey(2)); + } + + @Test + public void containsValue_positiveScenario_shouldReturnTrue() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertTrue(map.containsValue("Zero")); + } + + @Test + public void containsValue_byNotExistingValue_shouldSuccess() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertFalse(map.containsValue("Second")); + } + + @Test + public void keys_positiveScenario_shouldSuccess() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertArrayEquals(new long[]{0L, 1L}, map.keys()); + } + + @Test + public void keys_fromEmptyMap_shouldReturnEmptyArray() { + LongMap map = new LongMapImpl<>(); + + assertArrayEquals(new long[0], map.keys()); + } + + @Test + public void values_positiveScenario_shouldSuccess() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertArrayEquals(new String[]{"Zero", "First"}, map.values()); + } + + @Test + public void values_fromEmptyMap_shouldReturnEmptyArray() { + LongMap map = new LongMapImpl<>(); + + assertArrayEquals(new String[0], map.values()); + } + + @Test + public void size_positiveScenario_shouldSuccess() { + LongMap emptyMap = new LongMapImpl<>(); + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + assertEquals(0, emptyMap.size()); + assertEquals(2, map.size()); + } + + @Test + public void clear_positiveScenario_shouldSuccess() { + LongMap map = new LongMapImpl<>(); + map.put(0, "Zero"); + map.put(1, "First"); + + map.clear(); + assertEquals(0, map.size()); + assertArrayEquals(new long[0], map.keys()); + } +}