From c4f4faac52a0beecfde714a7990359556f3163ef Mon Sep 17 00:00:00 2001 From: Belov Date: Sat, 29 Apr 2023 16:56:28 +0300 Subject: [PATCH 1/6] LongMapImpl implementation --- .../opensource/longmap/LongMapImpl.java | 155 +++++++++++++++++- 1 file changed, 151 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 2f0b54b..5cd1c4c 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -1,43 +1,190 @@ package de.comparus.opensource.longmap; +import java.util.Arrays; + public class LongMapImpl implements LongMap { + + private static final int DEFAULT_CAPACITY = 20; + + private int size; + private int capacity; + private Entry[] table; + + public LongMapImpl() { + this.capacity = DEFAULT_CAPACITY; + this.table = new Entry[capacity]; + } + public V put(long key, V value) { + Entry[] table = this.table; + int index = indexFor((int) key, table.length); + + for (Entry e = table[index]; e != null; e = e.next) { + if (e.getKey() == key) { + e.value = value; + return null; + } + } + + Entry e = table[index]; + table[index] = new Entry<>(key, value, e); + if (++size > capacity) { + resize(table.length * 2); + } return null; } public V get(long key) { + int index = indexFor((int) key, table.length); + for (Entry e = table[index]; e != null; e = e.next) { + if (e.getKey() == key) { + return e.getValue(); + } + } return null; } public V remove(long key) { + int index = indexFor((int) key, table.length); + Entry prev = null; + Entry e = table[index]; + while (e != null) { + if (e.getKey() == key) { + if (prev == null) { + table[index] = e.next; + } else { + prev.next = e.next; + } + size--; + return e.value; + } + prev = e; + e = e.next; + } return null; } public boolean isEmpty() { - return false; + return size == 0; } public boolean containsKey(long key) { + int index = indexFor((int) key, table.length); + for (Entry e = table[index]; e != null; e = e.next) { + if (e.getKey() == key) { + return true; + } + } return false; } public boolean containsValue(V value) { + if (value == null) { + for (int i = 0; i < table.length; i++) { + for (Entry e = table[i]; e != null; e = e.next) { + if (e.value == null) { + return true; + } + } + } + } else { + for (int i = 0; i < table.length; i++) { + for (Entry e = table[i]; e != null; e = e.next) { + if (value.equals(e.value)) { + return true; + } + } + } + } return false; } public long[] keys() { - return null; + long[] result = new long[size]; + int i = 0; + for (Entry entry : table) { + while (entry != null) { + result[i++] = entry.key; + entry = entry.next; + } + } + return result; } public V[] values() { - return null; + V[] result = (V[]) new Object[size]; + int i = 0; + for (Entry e : table) { + while (e != null) { + result[i++] = e.value; + e = e.next; + } + } + return result; } public long size() { - return 0; + return size; } public void clear() { + Arrays.fill(table, null); + size = 0; + } + + private int indexFor(int hash, int length) { + return hash & (length - 1); + } + + private void resize(int newCapacity) { + Entry[] newTable = new Entry[newCapacity]; + + for (int i = 0; i < table.length; i++) { + Entry entry = table[i]; + while (entry != null) { + int newIndex = indexFor((int) entry.getKey(), newCapacity); + Entry next = entry.next; + + entry.next = newTable[newIndex]; + newTable[newIndex] = entry; + + entry = next; + } + } + + table = newTable; + capacity = (int) (newCapacity); + } + + private static class Entry { + final long key; + V value; + Entry next; + + Entry(long key, V value, Entry next) { + this.key = key; + this.value = value; + this.next = next; + } + + public long getKey() { + return key; + } + + public V getValue() { + return value; + } + + public void setValue(V value) { + this.value = value; + } + + public Entry getNext() { + return next; + } + public void setNext(Entry next) { + this.next = next; + } } } From 120e3d0533b7f3558ca4989f35b71400fbfeaa21 Mon Sep 17 00:00:00 2001 From: Belov Date: Sun, 30 Apr 2023 17:38:04 +0300 Subject: [PATCH 2/6] LongMapImpl refactoring --- .../comparus/opensource/longmap/LongMapImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 5cd1c4c..70e0810 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -17,7 +17,7 @@ public LongMapImpl() { public V put(long key, V value) { Entry[] table = this.table; - int index = indexFor((int) key, table.length); + int index = indexFor((int) key); for (Entry e = table[index]; e != null; e = e.next) { if (e.getKey() == key) { @@ -35,7 +35,7 @@ public V put(long key, V value) { } public V get(long key) { - int index = indexFor((int) key, table.length); + int index = indexFor((int) key); for (Entry e = table[index]; e != null; e = e.next) { if (e.getKey() == key) { return e.getValue(); @@ -45,7 +45,7 @@ public V get(long key) { } public V remove(long key) { - int index = indexFor((int) key, table.length); + int index = indexFor((int) key); Entry prev = null; Entry e = table[index]; while (e != null) { @@ -69,7 +69,7 @@ public boolean isEmpty() { } public boolean containsKey(long key) { - int index = indexFor((int) key, table.length); + int index = indexFor((int) key); for (Entry e = table[index]; e != null; e = e.next) { if (e.getKey() == key) { return true; @@ -132,8 +132,8 @@ public void clear() { size = 0; } - private int indexFor(int hash, int length) { - return hash & (length - 1); + private int indexFor(int hash) { + return Math.abs(hash); } private void resize(int newCapacity) { @@ -142,7 +142,7 @@ private void resize(int newCapacity) { for (int i = 0; i < table.length; i++) { Entry entry = table[i]; while (entry != null) { - int newIndex = indexFor((int) entry.getKey(), newCapacity); + int newIndex = indexFor((int)entry.getKey()); Entry next = entry.next; entry.next = newTable[newIndex]; From 158e04eb2def9b40e520398e38b8ccbea6fc0a4f Mon Sep 17 00:00:00 2001 From: Belov Date: Sun, 30 Apr 2023 17:38:45 +0300 Subject: [PATCH 3/6] LongMapImpl unit testing --- .../opensource/longmap/LongMapImplTest.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java 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..63387a6 --- /dev/null +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -0,0 +1,105 @@ +package de.comparus.opensource.longmap; + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; + +public class LongMapImplTest { + private LongMapImpl map; + + @Before + public void setUp() { + map = new LongMapImpl<>(); + } + + @Test + public void testPutAndGet() { + map.put(1, "one"); + map.put(2, "two"); + map.put(3, "three"); + map.put(-4, "minus four"); + map.put(-1, "minus one"); + + assertEquals("one", map.get(1)); + assertEquals("two", map.get(2)); + assertEquals("three", map.get(3)); + assertEquals("minus one", map.get(-1)); + assertEquals("minus four", map.get(-4)); + assertEquals(5, map.size()); + assertNull(map.get(5)); + } + + @Test + public void testRemove() { + map.put(1, "one"); + map.put(2, "two"); + map.put(3, "three"); + + assertEquals("two", map.remove(2)); + assertNull(map.get(2)); + assertEquals(2, map.size()); + assertNull(map.remove(4)); + } + + @Test + public void testIsEmpty() { + assertTrue(map.isEmpty()); + + map.put(1, "one"); + assertFalse(map.isEmpty()); + + map.remove(1); + assertTrue(map.isEmpty()); + } + + @Test + public void testContainsKey() { + map.put(1, "one"); + map.put(2, "two"); + map.put(3, "three"); + + assertTrue(map.containsKey(1)); + assertTrue(map.containsKey(2)); + assertTrue(map.containsKey(3)); + assertFalse(map.containsKey(4)); + } + + @Test + public void testContainsValue() { + map.put(1, "one"); + map.put(2, "two"); + map.put(3, "three"); + + assertTrue(map.containsValue("one")); + assertTrue(map.containsValue("two")); + assertTrue(map.containsValue("three")); + assertFalse(map.containsValue("four")); + } + + @Test + public void testKeys() { + map.put(1, "one"); + map.put(2, "two"); + map.put(3, "three"); + + long[] keys = map.keys(); + assertEquals(3, keys.length); + assertEquals(1, keys[0]); + assertEquals(2, keys[1]); + assertEquals(3, keys[2]); + } + + @Test + public void testValues() { + map.put(1, "one"); + map.put(2, "two"); + map.put(3, "three"); + + Object[] values = map.values(); + assertEquals(3, values.length); + assertEquals("one", values[0]); + assertEquals("two", values[1]); + assertEquals("three", values[2]); + } +} \ No newline at end of file From 4a8bacc1e594e82e6174eab0cb9b3afff761bcdc Mon Sep 17 00:00:00 2001 From: Belov Date: Mon, 1 May 2023 13:11:03 +0300 Subject: [PATCH 4/6] bag fixing and unit testing --- .../opensource/longmap/LongMapImpl.java | 35 ++++++++++++------- .../opensource/longmap/LongMapImplTest.java | 11 +++++- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 70e0810..6ecddc3 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -16,26 +16,33 @@ public LongMapImpl() { } public V put(long key, V value) { - Entry[] table = this.table; int index = indexFor((int) key); - + if (index >= table.length) { + resize(index * 2); + } + Entry[] table = this.table; for (Entry e = table[index]; e != null; e = e.next) { if (e.getKey() == key) { + V oldValue = e.value; e.value = value; - return null; + return oldValue; } } - - Entry e = table[index]; - table[index] = new Entry<>(key, value, e); if (++size > capacity) { resize(table.length * 2); + table = this.table; + index = indexFor((int) key); } + Entry e = table[index]; + table[index] = new Entry<>(key, value, e); return null; } public V get(long key) { int index = indexFor((int) key); + if (index > table.length) { + return null; + } for (Entry e = table[index]; e != null; e = e.next) { if (e.getKey() == key) { return e.getValue(); @@ -46,6 +53,9 @@ public V get(long key) { public V remove(long key) { int index = indexFor((int) key); + if (index > table.length) { + return null; + } Entry prev = null; Entry e = table[index]; while (e != null) { @@ -70,6 +80,9 @@ public boolean isEmpty() { public boolean containsKey(long key) { int index = indexFor((int) key); + if (index > table.length) { + return false; + } for (Entry e = table[index]; e != null; e = e.next) { if (e.getKey() == key) { return true; @@ -138,11 +151,10 @@ private int indexFor(int hash) { private void resize(int newCapacity) { Entry[] newTable = new Entry[newCapacity]; - - for (int i = 0; i < table.length; i++) { - Entry entry = table[i]; + for (Entry vEntry : table) { + Entry entry = vEntry; while (entry != null) { - int newIndex = indexFor((int)entry.getKey()); + int newIndex = indexFor((int) entry.getKey()); Entry next = entry.next; entry.next = newTable[newIndex]; @@ -151,9 +163,8 @@ private void resize(int newCapacity) { entry = next; } } - table = newTable; - capacity = (int) (newCapacity); + capacity = newCapacity; } private static class Entry { diff --git a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java index 63387a6..8c28af2 100644 --- a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -20,14 +20,21 @@ public void testPutAndGet() { map.put(3, "three"); map.put(-4, "minus four"); map.put(-1, "minus one"); + map.put(20, "twenty"); + map.put(50, "fifty"); + map.put(-500, "minus five hundred"); assertEquals("one", map.get(1)); assertEquals("two", map.get(2)); assertEquals("three", map.get(3)); assertEquals("minus one", map.get(-1)); assertEquals("minus four", map.get(-4)); - assertEquals(5, map.size()); + assertEquals("twenty", map.get(20)); + assertEquals("fifty", map.get(50)); + assertEquals("minus five hundred", map.get(-500)); + assertEquals(8, map.size()); assertNull(map.get(5)); + assertNull(map.get(500)); } @Test @@ -40,6 +47,7 @@ public void testRemove() { assertNull(map.get(2)); assertEquals(2, map.size()); assertNull(map.remove(4)); + assertNull(map.remove(500)); } @Test @@ -63,6 +71,7 @@ public void testContainsKey() { assertTrue(map.containsKey(2)); assertTrue(map.containsKey(3)); assertFalse(map.containsKey(4)); + assertFalse(map.containsKey(500)); } @Test From 6430adda088bbbe1d2d453a534d63fa74a6f7b2d Mon Sep 17 00:00:00 2001 From: Belov Date: Mon, 1 May 2023 13:19:06 +0300 Subject: [PATCH 5/6] unit testing --- .../comparus/opensource/longmap/LongMapImplTest.java | 10 ++++++++-- 1 file changed, 8 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 8c28af2..00c9baa 100644 --- a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -23,6 +23,7 @@ public void testPutAndGet() { map.put(20, "twenty"); map.put(50, "fifty"); map.put(-500, "minus five hundred"); + map.put(0, null); assertEquals("one", map.get(1)); assertEquals("two", map.get(2)); @@ -32,7 +33,8 @@ public void testPutAndGet() { assertEquals("twenty", map.get(20)); assertEquals("fifty", map.get(50)); assertEquals("minus five hundred", map.get(-500)); - assertEquals(8, map.size()); + assertEquals(9, map.size()); + assertNull(map.get(0)); assertNull(map.get(5)); assertNull(map.get(500)); } @@ -79,10 +81,12 @@ public void testContainsValue() { map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); + map.put(10, null); assertTrue(map.containsValue("one")); assertTrue(map.containsValue("two")); assertTrue(map.containsValue("three")); + assertTrue(map.containsValue(null)); assertFalse(map.containsValue("four")); } @@ -104,11 +108,13 @@ public void testValues() { map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); + map.put(10, null); Object[] values = map.values(); - assertEquals(3, values.length); + assertEquals(4, values.length); assertEquals("one", values[0]); assertEquals("two", values[1]); assertEquals("three", values[2]); + assertNull(values[3]); } } \ No newline at end of file From 7c93717b3a2d95aeb18eda67c50cdae4277ee16f Mon Sep 17 00:00:00 2001 From: Belov Date: Mon, 1 May 2023 13:43:21 +0300 Subject: [PATCH 6/6] refactoring and unit testing --- .../opensource/longmap/LongMapImpl.java | 12 -------- .../opensource/longmap/LongMapImplTest.java | 30 +++++++++++++------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 6ecddc3..c33ac5f 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -185,17 +185,5 @@ public long getKey() { public V getValue() { return value; } - - public void setValue(V value) { - this.value = value; - } - - public Entry getNext() { - return next; - } - - public void setNext(Entry next) { - this.next = next; - } } } diff --git a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java index 00c9baa..50ae13c 100644 --- a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -95,12 +95,18 @@ public void testKeys() { map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); + map.put(-1, "minus one"); + map.put(-2, "minus two"); + map.put(-3, "minus three"); long[] keys = map.keys(); - assertEquals(3, keys.length); - assertEquals(1, keys[0]); - assertEquals(2, keys[1]); - assertEquals(3, keys[2]); + assertEquals(6, keys.length); + assertEquals(-1, keys[0]); + assertEquals(1, keys[1]); + assertEquals(-2, keys[2]); + assertEquals(2, keys[3]); + assertEquals(-3, keys[4]); + assertEquals(3, keys[5]); } @Test @@ -108,13 +114,19 @@ public void testValues() { map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); + map.put(-1, "minus one"); + map.put(-2, "minus two"); + map.put(-3, "minus three"); map.put(10, null); Object[] values = map.values(); - assertEquals(4, values.length); - assertEquals("one", values[0]); - assertEquals("two", values[1]); - assertEquals("three", values[2]); - assertNull(values[3]); + assertEquals(7, values.length); + assertEquals("minus one", values[0]); + assertEquals("one", values[1]); + assertEquals("minus two", values[2]); + assertEquals("two", values[3]); + assertEquals("minus three", values[4]); + assertEquals("three", values[5]); + assertNull(values[6]); } } \ No newline at end of file