diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 2f0b54b..c33ac5f 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -1,43 +1,189 @@ 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) { + 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 oldValue; + } + } + 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(); + } + } return null; } 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) { + 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); + if (index > table.length) { + return false; + } + 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) { + return Math.abs(hash); + } + + private void resize(int newCapacity) { + Entry[] newTable = new Entry[newCapacity]; + for (Entry vEntry : table) { + Entry entry = vEntry; + while (entry != null) { + int newIndex = indexFor((int) entry.getKey()); + Entry next = entry.next; + + entry.next = newTable[newIndex]; + newTable[newIndex] = entry; + + entry = next; + } + } + table = newTable; + capacity = 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; + } } } 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..50ae13c --- /dev/null +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -0,0 +1,132 @@ +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"); + 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)); + assertEquals("three", map.get(3)); + assertEquals("minus one", map.get(-1)); + assertEquals("minus four", map.get(-4)); + assertEquals("twenty", map.get(20)); + assertEquals("fifty", map.get(50)); + assertEquals("minus five hundred", map.get(-500)); + assertEquals(9, map.size()); + assertNull(map.get(0)); + assertNull(map.get(5)); + assertNull(map.get(500)); + } + + @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)); + assertNull(map.remove(500)); + } + + @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)); + assertFalse(map.containsKey(500)); + } + + @Test + 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")); + } + + @Test + 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(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 + 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(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