diff --git a/.gitignore b/.gitignore index a870eaf..4f94b89 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -.idea/ -long-map.iml \ No newline at end of file +.idea/* +*.iml +target/* \ No newline at end of file diff --git a/pom.xml b/pom.xml index 36c092b..88d2c48 100644 --- a/pom.xml +++ b/pom.xml @@ -18,8 +18,8 @@ 3.3 UTF-8 - 1.8 - 1.8 + 9 + 9 @@ -27,10 +27,16 @@ - junit - junit - 4.12 + org.junit.jupiter + junit-jupiter-engine + 5.4.0 test + + org.junit.jupiter + junit-jupiter-api + 5.4.0 + compile + diff --git a/src/main/java/de/comparus/opensource/longmap/LongMap.java b/src/main/java/de/comparus/opensource/longmap/LongMap.java index adbf242..4612631 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMap.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMap.java @@ -2,16 +2,22 @@ public interface LongMap { V put(long key, V value); + V get(long key); + V remove(long key); boolean isEmpty(); + boolean containsKey(long key); + boolean containsValue(V value); long[] keys(); + V[] values(); long size(); + 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..1de694a 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -1,43 +1,180 @@ package de.comparus.opensource.longmap; +import java.util.Objects; + public class LongMapImpl implements LongMap { + + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final int DEFAULT_INCREASE_CAPACITY = 2; + private static final float DEFAULT_LOAD_FACTOR = 0.75F; + private int size; + private float threshold; + private Node[] table; + + public LongMapImpl() { + threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); + table = new Node[DEFAULT_INITIAL_CAPACITY]; + } + + private static class Node { + private V value; + private long key; + private Node next; + + public Node(V value, long key, Node next) { + this.value = value; + this.key = key; + this.next = next; + } + } + public V put(long key, V value) { - return null; + if (threshold < size) { + resize(); + } + Node[] currentTable = table; + int index = getIndex(key); + if (currentTable[index] == null) { + currentTable[index] = new Node<>(value, key, null); + size++; + return value; + } + + Node currentNode = currentTable[index]; + while (currentNode.next != null || key == currentNode.key) { + if (key == currentNode.key) { + currentNode.value = value; + return value; + } + currentNode = currentNode.next; + } + + currentNode.next = new Node<>(value, key, null); + size++; + + return value; } public V get(long key) { + Node currentNode = table[getIndex(key)]; + while (currentNode != null) { + if (key == currentNode.key) { + return currentNode.value; + } + currentNode = currentNode.next; + } return null; } public V remove(long key) { + int index = getIndex(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.next != null) { + if (currentNode.next.key == key) { + V value = currentNode.next.value; + currentNode.next = currentNode.next.next; + size--; + return value; + } + currentNode = currentNode.next; + } return null; } public boolean isEmpty() { - return false; + return size == 0; } public boolean containsKey(long key) { + Node currentNode = table[getIndex(key)]; + while (currentNode != null) { + if (key == currentNode.key) { + return true; + } + currentNode = currentNode.next; + } return false; } public boolean containsValue(V value) { + Node[] currentTable = table; + for (Node vNode : currentTable) { + Node currentNode = vNode; + while (currentNode != null) { + if (Objects.equals(value, currentNode.value)) { + return true; + } + currentNode = currentNode.next; + } + } return false; } public long[] keys() { - return null; + Node[] currentTable = table; + long[] keysArray = new long[size]; + int k = 0; + for (Node vNode : currentTable) { + Node currentNode = vNode; + while (currentNode != null) { + keysArray[k++] = currentNode.key; + currentNode = currentNode.next; + } + } + return keysArray; } public V[] values() { - return null; + Node[] currentTable = table; + V[] valuesArray = (V[]) new Object[size]; + int k = 0; + for (Node vNode : currentTable) { + Node currentNode = vNode; + while (currentNode != null) { + valuesArray[k++] = currentNode.value; + currentNode = currentNode.next; + } + } + return valuesArray; } public long size() { - return 0; + return size; } public void clear() { + Node[] currentTable = table; + if (currentTable != null && size > 0) { + size = 0; + for (int i = 0; i < currentTable.length; i++) { + currentTable[i] = null; + } + } + } + + private int getIndex(long key) { + return (int) Math.abs(key % table.length); + } + private void resize() { + Node[] oldTable = table; + size = 0; + table = new Node[oldTable.length * DEFAULT_INCREASE_CAPACITY]; + threshold = table.length * DEFAULT_LOAD_FACTOR; + for (int i = 0; i < oldTable.length; i++) { + Node nodeOld = oldTable[i]; + while (nodeOld != null) { + put(nodeOld.key, nodeOld.value); + nodeOld = nodeOld.next; + } + } } } diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImplTest.java b/src/main/java/de/comparus/opensource/longmap/LongMapImplTest.java new file mode 100644 index 0000000..62c038a --- /dev/null +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -0,0 +1,139 @@ +package de.comparus.opensource.longmap; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class LongMapImplTest { + + @Test + public void testMethodSize_Ok() { + LongMap map = new LongMapImpl<>(); + map.put(1L, "First"); + assertEquals(1, map.size()); + map.put(2L, "Second"); + assertEquals(2, map.size()); + map.put(17L, "Third"); + assertEquals(3, map.size()); + map.put(-100L, "Forth"); + assertEquals(4, map.size()); + } + + @Test + public void testMethodPut_Ok() { + LongMap map = new LongMapImpl<>(); + assertEquals("first", map.put(20L, "first")); + assertEquals("second", map.put(120L, "second")); + assertEquals("third", map.put(-120L, "third")); + assertEquals("forth", map.put(0L, "forth")); + assertEquals("fifth", map.put(1L, "fifth")); + assertEquals("One hundred", map.put(8L, "One hundred")); + } + + @Test + public void testMethodPutForCorrectWorkSameKey_Ok() { + LongMap map = new LongMapImpl<>(); + assertEquals(10, map.put(100L, 10)); + assertEquals(20, map.put(100L, 20)); + assertEquals(-10020, map.put(100L, -10020)); + assertNull(map.put(100L, null)); + } + + @Test + public void testMethodGet_Ok() { + LongMap map = new LongMapImpl<>(); + assertEquals(map.put(20L, "first"), map.get(20L)); + assertEquals(map.put(-20L, "second"), map.get(-20L)); + assertEquals(map.put(120L, "third"), map.get(120L)); + assertEquals(map.put(220L, null), map.get(220L)); + assertNull(map.get(-123L)); + } + + @Test + public void testMethodRemove_Ok() { + LongMap map = new LongMapImpl<>(); + assertEquals(map.put(20L, "first"), map.remove(20L)); + assertEquals(map.put(-20L, "second"), map.remove(-20L)); + assertEquals(map.put(120L, "third"), map.remove(120L)); + assertNull(map.remove(120L)); + assertEquals(map.put(220L, null), map.remove(220L)); + assertNull(map.remove(-11220L)); + map.put(20L, "first"); + map.put(52L, "second"); + assertEquals(map.put(36L, "third"), map.remove(36L)); + } + + @Test + public void testMethodIsEmpty_Ok() { + LongMap map = new LongMapImpl<>(); + assertTrue(map.isEmpty()); + map.put(10L, "value"); + assertFalse(map.isEmpty()); + } + + @Test + public void testMethodClear_Ok() { + LongMap map = new LongMapImpl<>(); + map.clear(); + assertEquals(0, map.size()); + map.put(10L, 1); + map.put(1L, 10); + assertNotEquals(0, map.size()); + map.clear(); + assertEquals(0, map.size()); + } + + @Test + public void testMethodContainKey_Ok() { + LongMap map = new LongMapImpl<>(); + map.put(0L, 0); + assertTrue(map.containsKey(0)); + assertFalse(map.containsKey(1000L)); + } + + @Test + public void testMethodContainValue_Ok() { + LongMap map = new LongMapImpl<>(); + map.put(100L, "One"); + assertTrue(map.containsValue("One")); + assertFalse(map.containsValue("Three")); + } + + @Test + public void testMethodKeys_Ok() { + LongMap map = new LongMapImpl<>(); + map.put(20L, "first"); + map.put(36L, "first"); + map.put(19L, "first"); + map.put(-20L, "second"); + map.put(120L, "third"); + map.put(220L, null); + long[] expected = new long[]{19L, 20L, 36L, -20L, 120L, 220L}; + long[] actual = map.keys(); + Assertions.assertArrayEquals(expected, actual); + } + + @Test + public void testMethodValues_Ok() { + LongMap map = new LongMapImpl<>(); + Integer[] expected = new Integer[10]; + for (int i = 0; i < 10; i++) { + map.put(i, i); + expected[i] = i; + } + Assertions.assertArrayEquals(expected, map.values()); + } + + @Test + public void testForResize_Ok() { + LongMap map = new LongMapImpl<>(); + for (int i = 0; i < 10000; i++) { + map.put(i, i); + } + assertEquals(10000, map.size()); + } +}