From 7f35cc6ac0d5e161951fd8cc092918544f16712d Mon Sep 17 00:00:00 2001 From: Mykhailo Tereshchenkov Date: Fri, 21 Jan 2022 12:16:21 +0200 Subject: [PATCH 1/3] Development of LongMapImpl class was finished, all required methods were implemented --- .../comparus/opensource/longmap/LongMap.java | 6 + .../opensource/longmap/LongMapImpl.java | 161 +++++++++++++++++- .../opensource/longmap/LongMapImplTest.java | 123 +++++++++++++ 3 files changed, 284 insertions(+), 6 deletions(-) create mode 100644 src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java 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..612e080 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -1,43 +1,192 @@ package de.comparus.opensource.longmap; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + public class LongMapImpl implements LongMap { + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + private static final int DEFAULT_RESIZE_MULTIPLIER = 2; + private int size; + private int threshold; + private Node[] table; + + public LongMapImpl() { + threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); + table = new Node[DEFAULT_INITIAL_CAPACITY]; + size = 0; + } + public V put(long key, V value) { - return null; + if (size >= threshold) { + resize(); + } + int index = getIndex(key); + Node newNode = new Node<>(key, value); + if (table[index] == null) { + table[index] = newNode; + } else { + Node currentNode = table[index]; + while (currentNode != null) { + if (Objects.equals(key, currentNode.key)) { + currentNode.value = value; + return value; + } + if (currentNode.next == null) { + currentNode.next = newNode; + break; + } + currentNode = currentNode.next; + } + } + size++; + return value; } public V get(long key) { + int index = getIndex(key); + Node node = table[index]; + while (node != null) { + if (Objects.equals(key, node.key)) { + return node.value; + } + node = node.next; + } return null; } public V remove(long key) { - return null; + int index = getIndex(key); + if (table[index] == null) { + return null; + } + int counter = 0; + Node node = table[index]; + while (node != null) { + if (node.key.equals(key)) { + break; + } + counter++; + node = node.next; + } + V value = null; + if (counter == 0) { + value = table[index].value; + table[index] = table[index].next; + } else { + node = table[index]; + for (int i = 0; i < counter - 1; i++) { + node = node.next; + } + value = node.next.value; + node.next = node.next.next; + } + size--; + return value; } public boolean isEmpty() { - return false; + return size == 0; } public boolean containsKey(long key) { + int index = getIndex(key); + Node node = table[index]; + while (node != null) { + if (node.key.equals(key)) { + return true; + } + node = node.next; + } return false; } public boolean containsValue(V value) { + for (int i = 0; i < table.length; i++) { + Node node = table[i]; + while (node != null) { + if (node.value.equals(value)) { + return true; + } + node = node.next; + } + } return false; } public long[] keys() { - return null; + long[] result = new long[(int)size()]; + int index = 0; + for (int i = 0; i < table.length; i++) { + Node node = table[i]; + while (node != null) { + result[index] = node.key; + index++; + node = node.next; + } + } + return result; } public V[] values() { - return null; + List list = new ArrayList<>(); + for (int i = 0; i < table.length; i++) { + Node node = table[i]; + while (node != null) { + list.add(node.value); + node = node.next; + } + } + V[] result = (V[]) java.lang.reflect.Array.newInstance(list.get(0) + .getClass(), list.size()); + for (int i = 0; i < list.size(); i++) { + result[i] = list.get(i); + } + return result; } public long size() { - return 0; + return size; } public void clear() { + if (table != null && size > 0) { + size = 0; + for (int i = 0; i < table.length; ++i) { + table[i] = null; + } + } + System.gc(); + } + + private int getIndex(Long key) { + return (key == null) ? 0 : Math.abs(Objects.hash(key) % table.length); + } + + private void resize() { + Node[] oldTable = table; + int newCapacity = oldTable.length * DEFAULT_RESIZE_MULTIPLIER; + threshold = (int) (newCapacity * DEFAULT_LOAD_FACTOR); + table = new Node[newCapacity]; + size = 0; + for (Node node : oldTable) { + while (node != null) { + put(node.key, node.value); + node = node.next; + } + } + } + + private static class Node { + private final Long key; + private V value; + private Node next; + private Node(Long key, V value) { + this.key = key; + this.value = value; + this.next = null; + } } } 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..49f66e2 --- /dev/null +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -0,0 +1,123 @@ +package de.comparus.opensource.longmap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class LongMapImplTest { + private LongMap map; + + @Before + public void initializeMap() { + map = new LongMapImpl<>(); + } + + @Test + public void put_validBehavior_ok() { + String expected = "first value"; + String actual = map.put(1L, expected); + assertEquals(expected, actual); + } + + @Test + public void get_validBehavior_ok() { + String expected = "first value"; + map.put(1, expected); + String actual = map.get(1L); + assertEquals(expected, actual); + } + + @Test + public void get_invalidBehavior_notOk() { + assertNull(map.get(1L)); + } + + @Test + public void remove_validBehavior_ok() { + map.put(1L, "first value"); + assertNotNull(map.remove(1L)); + } + + @Test + public void remove_invalidBehavior_notOk() { + assertNull(map.remove(1L)); + } + + @Test + public void isEmpty_validBehavior_ok() { + assertTrue(map.isEmpty()); + } + + @Test + public void isEmpty_invalidBehavior_notOk() { + map.put(1L, "first value"); + assertFalse(map.isEmpty()); + } + + @Test + public void containsKey_validBehavior_ok() { + map.put(1, "first value"); + assertTrue(map.containsKey(1L)); + } + + @Test + public void containsKey_invalidBehavior_notOk() { + assertFalse(map.containsKey(1L)); + } + + @Test + public void containsValue_validBehavior_ok() { + map.put(1L, "first value"); + assertTrue(map.containsValue("first value")); + } + + @Test + public void containsValue_invalidBehavior_notOk() { + assertFalse(map.containsValue("first value")); + } + + @Test + public void keys_validBehavior_ok() { + Long[] expected = new Long[] {1L, 2L, 3L}; + map.put(expected[0], "first value"); + map.put(expected[1], "second value"); + map.put(expected[2], "third value"); + long[] actual = map.keys(); + for (int i = 0; i < 3; i++) { + assertEquals(expected[i], (Long)actual[i]); + } + } + + @Test + public void values_validBehavior_ok() { + String[] expected = new String[] {"first value", "second value", "third value"}; + map.put(1L, "first value"); + map.put(2L, "second value"); + map.put(3L, "third value"); + String[] actual = map.values(); + for (int i = 0; i < 3; i++) { + assertEquals(expected[i], actual[i]); + } + } + + @Test + public void size_validBehavior_ok() { + map.put(1L, "first value"); + map.put(2L, "second value"); + map.put(3L, "third value"); + assertEquals(3L, map.size()); + } + + @Test + public void clear_validBehavior_ok() { + map.put(1L, "first value"); + map.clear(); + assertNull(map.get(1L)); + } +} From cb18783d747351c195299c9ffb2bb3878d7c8d04 Mon Sep 17 00:00:00 2001 From: Mykhailo Tereshchenkov Date: Fri, 21 Jan 2022 12:43:43 +0200 Subject: [PATCH 2/3] Collision test added --- .../comparus/opensource/longmap/LongMapImplTest.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java index 49f66e2..4c16e6b 100644 --- a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -5,8 +5,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; - -import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -25,6 +23,15 @@ public void put_validBehavior_ok() { assertEquals(expected, actual); } + @Test + public void put_collision_ok() { + String expected = "third value"; + map.put(1L, "first value"); + map.put(17L, "second value"); + String actual = map.put(33L, expected); + assertEquals(expected, actual); + } + @Test public void get_validBehavior_ok() { String expected = "first value"; From 3d4b4888421e0c80b95415613d561b8851a050f1 Mon Sep 17 00:00:00 2001 From: Mykhailo Tereshchenkov Date: Fri, 21 Jan 2022 12:49:08 +0200 Subject: [PATCH 3/3] Performance fix for test cases --- .../de/comparus/opensource/longmap/LongMapImplTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java index 4c16e6b..92eae2c 100644 --- a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -5,6 +5,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; + +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -16,6 +18,11 @@ public void initializeMap() { map = new LongMapImpl<>(); } + @After + public void destroyMap() { + System.gc(); + } + @Test public void put_validBehavior_ok() { String expected = "first value";