From 5df2aadb170c7239f45b362e5408a6c729e6781d Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 00:29:46 +0300 Subject: [PATCH 01/21] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 7bb1716..a0efac4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ # long-map Finish development of class LongMapImpl, which implements a map with keys of type long. It has to be a hash table (like HashMap). Requirements: -* it should not use any known Map implementations; -* it should use as less memory as possible and have adequate performance; -* the main aim is to see your codestyle and teststyle From 4c04e6993714c02527808cc4271b4e0dd7032803 Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 00:35:33 +0300 Subject: [PATCH 02/21] Create LongMap interface --- src/main/java/de/comparus/opensource/longmap/LongMap.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMap.java b/src/main/java/de/comparus/opensource/longmap/LongMap.java index adbf242..471119b 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMap.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMap.java @@ -1,5 +1,7 @@ package de.comparus.opensource.longmap; +import java.util.Map; + public interface LongMap { V put(long key, V value); V get(long key); From 391b8ef7088359501d21e52006d258d48a14e420 Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 00:35:55 +0300 Subject: [PATCH 03/21] Create LongMapImpl class --- .../opensource/longmap/LongMapImpl.java | 299 +++++++++++++++++- 1 file changed, 291 insertions(+), 8 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..b2b7d6e 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -1,43 +1,326 @@ package de.comparus.opensource.longmap; +import java.lang.reflect.Array; +import java.util.*; + public class LongMapImpl implements LongMap { + private final int DEFAULT_INITIAL_CAPACITY; + private final float LOAD_FACTOR; + + private transient LongEntry[] table; + /** The total number of entries in the map.*/ + private int count; + /** The table is resized when its size exceeds this threshold. The value of this field is (int)(capacity * loadFactor)*/ + private int threshold; + + public LongMapImpl() { + this(16, 0.75f); + } + + /** + * Constructs a new map with the given LongMap. + * Initial capacity - 16. + * Load factor - 0.75. + * @throws NullPointerException – if the given longMap is null. + */ + public LongMapImpl(LongMap longMap) { + this((int) (2*longMap.size()), 0.75f); + putAll(longMap); + } + + public LongMapImpl(int initialCapacity, float loadFactor) { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal argument capacity: " + initialCapacity); + if (loadFactor < 0 || Float.isNaN(loadFactor)) + throw new IllegalArgumentException("Illegal load capacity: " + loadFactor); + + if (initialCapacity==0) + initialCapacity = 1; + + DEFAULT_INITIAL_CAPACITY = initialCapacity; + LOAD_FACTOR = loadFactor; + threshold = (int) (DEFAULT_INITIAL_CAPACITY * LOAD_FACTOR); + table = new LongEntry[DEFAULT_INITIAL_CAPACITY]; + count = 0; + } + + /** + * If number of keys in the hashtable exceeds threshold + * method recalculates the index of every key in the table. + */ + @Override public V put(long key, V value) { - return null; + if (count >= threshold) { + resize(); + } + LongEntry newEntry = new LongEntry<>(key, value); + int index = calculateIndex(key); + return putEntry(newEntry, index); } + private V putEntry(LongEntry newEntry, int index) { + if (table[index] == null) { + table[index] = newEntry; + count++; + } else { + for (LongEntry entry = table[index]; entry != null;) { + if (entry.getKey() == newEntry.getKey()) { + entry.value = newEntry.value; + return newEntry.value; + } else if (entry.next == null) { + entry.next = newEntry; + count++; + break; + } + entry = entry.next; + } + } + return newEntry.value; + } + + /** + * Copies all the entries from the longMap map to this table. + */ + public void putAll(LongMap longMap) { + if (longMap != null) { + long[] keys = longMap.keys(); + for (int i = keys.length; i-- > 0; ) { + put(keys[i], longMap.get(keys[i])); + } + } + } + + + private int calculateIndex(long key) { + if (key == 0) + return 0; + else + return (Long.hashCode(key) & 0x7FFFFFFF) % table.length; + } + + /** + * Resizes table and increases the capacity of table. + * Recalculates the index of every key in order to make map operations more efficient. + * This method is called automatically when the + * number of keys in the hashtable exceeds threshold. + */ + private void resize() { + int oldSize = this.table.length; + int newSize = oldSize * 2; + LongEntry[] newTable = new LongEntry[newSize]; + LongEntry[] oldTable = this.table; + + this.threshold = (int)(newSize * LOAD_FACTOR); + this.table = newTable; + + for (int i = oldSize; i-- > 0;) { + for (LongEntry entry = oldTable[i]; entry != null;) { + int index = calculateIndex(entry.getKey()); + LongEntry next = entry.next; + if (table[index] == null) { + table[index] = entry; + table[index].next = null; + } else { + for (LongEntry newTableEntry = table[index]; newTableEntry != null;) { + if (newTableEntry.next == null) { + newTableEntry.next = entry; + newTableEntry.next.next = null; + } + newTableEntry = newTableEntry.next; + } + } + entry = next; + } + } + } + + @Override public V get(long key) { + int index = calculateIndex(key); + for (LongEntry entry = table[index]; entry != null;) { + if (key == entry.key) { + return entry.value; + } + entry = entry.next; + } return null; } + @Override public V remove(long key) { - return null; + int index = calculateIndex(key); + if (table[index] == null) + return null; + + V value = table[index].value; + + if (table[index].next == null) + table[index] = null; + + LongEntry previous = null; + for (LongEntry entry = table[index]; entry != null;) { + if (key == entry.key) { + if (previous == null) { + table[index] = table[index].next; + } else if (entry.next == null) { + previous.next = null; + } else { + previous.next = entry.next; + entry = null; + break; + } + } + previous = entry; + entry = entry.next; + } + count--; + return value; } + @Override public boolean isEmpty() { - return false; + return count == 0; } + @Override public boolean containsKey(long key) { - return false; + return get(key) != null; } + @Override public boolean containsValue(V value) { + for (int i = count; i-- > 0 ;) { + for (LongEntry entry = table[i]; entry != null ; entry = entry.next) { + if (entry.value.equals(value)) { + return true; + } + } + } return false; } + @Override public long[] keys() { - return null; + long[] keys = new long[count]; + int keysIndex = 0; + + for (int i = table.length; i-- > 0;) { + for (LongEntry entry = table[i]; entry != null;) { + keys[keysIndex] = entry.key; + keysIndex++; + entry = entry.next; + } + } + return keys; } - public V[] values() { - return null; + + /** + * @throws ArrayIndexOutOfBoundsException if map is empty + */ + @Override + public V[] values() throws ArrayIndexOutOfBoundsException { + V[] values = (V[]) new Object[count]; + int valueIndex = 0; + for (int i = table.length; i-- > 0;) { + for (LongEntry entry = table[i]; entry != null;) { + values[valueIndex] = entry.value; + valueIndex++; + entry = entry.next; + } + } + return (V[]) Array.newInstance(values[0].getClass(), values.length); } + @Override public long size() { - return 0; + return count; } + @Override public void clear() { + for (int i = table.length; i-- > 0;) { + table[i] = null; + } + count = 0; + System.gc(); + } + + public Set> entrySet() { + Set> entrySet = new HashSet<>(); + for (int i = table.length; i-- > 0;) { + for (LongEntry entry = table[i]; entry != null;) { + entrySet.add(entry); + entry = entry.next; + } + } + return entrySet; + } + + @Override + public int hashCode() { + return Arrays.hashCode(keys()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LongMapImpl longMap = (LongMapImpl) o; + return Arrays.equals(keys(), longMap.keys()); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("{"); + for (int i = table.length; i-- > 0;) { + for (LongEntry entry = table[i]; entry != null;) { + stringBuilder.append(entry.toString()); + stringBuilder.append(", "); + entry = entry.next; + if (i - 1 == 0 && entry == null) + return stringBuilder.replace(stringBuilder.length()-2, stringBuilder.length(), "") + .append("}").toString(); + } + } + return "{}"; + } + + private static class LongEntry { + final long key; + V value; + LongEntry next; + + LongEntry(long key, V value) { + this.key = key; + this.value = value; + } + + public long getKey() { + return key; + } + + public V getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LongEntry longEntry = (LongEntry) o; + return key == longEntry.key; + } + + @Override + public int hashCode() { + return Objects.hash(key); + } + @Override + public String toString() { + return this.key + "=" + this.value; + } } } From d735853a96268034e53a27a83ccbeaee20b379a6 Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 00:36:43 +0300 Subject: [PATCH 04/21] Added JUnit dependency --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 36c092b..ac33188 100644 --- a/pom.xml +++ b/pom.xml @@ -32,5 +32,10 @@ 4.12 test + + junit + junit + 4.13.2 + From 66e5a1ebb26d8683c5659a7d51b5ebabddc8f1fa Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 00:37:42 +0300 Subject: [PATCH 05/21] Created LongMapImplTest --- .../opensource/longmap/LongMapImplTest.java | 278 ++++++++++++++++++ 1 file changed, 278 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..4cfd56b --- /dev/null +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -0,0 +1,278 @@ +package de.comparus.opensource.longmap; + +import org.junit.Before; +import org.junit.Test; + +public class LongMapImplTest { + LongMapImpl longMap; + @Before + public void before() { + longMap = new LongMapImpl<>(); + } + + @Test + public void putEntry_Test() { + longMap.put(1000000000l, "Str"); + longMap.put(1000000001l, "Str1"); + longMap.put(1000000002l, "Str2"); + + assert longMap.get(1000000000l).equals("Str"); + assert longMap.get(1000000001l).equals("Str1"); + assert longMap.get(1000000002l).equals("Str2"); + assert longMap.size() == 3; + } + + @Test + public void putDuplicateValue_Test() { + longMap.put(1000000000l, "Str"); + longMap.put(1000000000l, "Str1"); + longMap.put(1000000000l, "Str2"); + longMap.put(1000000000l, "Str3"); + + assert longMap.get(1000000000l).equals("Str3"); + assert longMap.size() == 1; + } + + @Test + public void putDuplicateNullValue_Test() { + longMap.put(0, "Str"); + longMap.put(0, "Str1"); + longMap.put(0, "Str2"); + + assert longMap.get(0).equals("Str2"); + assert longMap.size() == 1; + } + + + @Test + public void put101Entries_Test() { + for (int i = 0; i < 101; i++) { + longMap.put((i * 3331111), "i = " + i); + } + assert longMap.size() == 101; + for (int i = 0; i < 101; i++) { + assert longMap.get(i * 3331111).equals("i = " + i); + } + } + + @Test + public void removeEntry_Test() { + longMap.put(1000000000l, "Str"); + longMap.put(1000000001l, "Str1"); + longMap.put(1000000002l, "Str2"); + + assert longMap.remove(1000000000l).equals("Str"); + assert longMap.remove(1000000001l).equals("Str1"); + assert longMap.remove(1000000002l).equals("Str2"); + assert longMap.size() == 0; + } + + @Test + public void removeEntry_NotOkTest() { + longMap.put(1000000000l, "Str"); + + assert longMap.remove(1000000001l) == null; + assert longMap.size() == 1; + } + + @Test + public void removeEntryFromEmptyMap_Test() { + assert longMap.remove(1000000001l) == null; + assert longMap.size() == 0; + } + + @Test + public void remove101Entries_Test() { + for (int i = 0; i < 101; i++) { + longMap.put((i * 3331111), "i = " + i); + } + assert longMap.size() == 101; + for (int i = 0; i < 101; i++) { + longMap.remove(i * 3331111).equals("i = " + i); + } + assert longMap.size() == 0; + } + + @Test + public void keys_Test() { + longMap = new LongMapImpl<>(); + longMap.put(1000000000l, "Str"); + + assert longMap.keys()[0] == 1000000000l; + assert longMap.size() == 1; + } + + @Test + public void keys101_Test() { + for (int i = 0; i < 101; i++) { + longMap.put((i * 3331111), "i = " + i); + } + long[] keys = longMap.keys(); + assert keys.length == 101; + for (int i = 0; i < keys.length; i++) { + longMap.remove(keys[i]); + } + assert longMap.size() == 0; + } + + @Test + public void values_Test() { + longMap.put(1000000000l, "Str"); + + assert longMap.keys()[0] == 1000000000l; + assert longMap.size() == 1; + } + + @Test + public void values101_Test() { + for (int i = 0; i < 101; i++) { + longMap.put((i * 3331111), "i = " + i); + } + String[] values = longMap.values(); + assert values.length == 101; + } + + @Test + public void containsKeyTest() { + longMap.put(1000000000l, "Str"); + + assert longMap.containsKey(1000000000l); + assert !longMap.containsKey(1000000001l); + } + + @Test + public void containsKey_NotOkTest() { + longMap.put(1000000000l, "Str"); + + assert !longMap.containsKey(1000000001l); + assert longMap.size() == 1; + } + + @Test + public void containsKey101_Test() { + for (int i = 0; i < 101; i++) { + longMap.put((i * 3331111), "i = " + i); + } + for (int i = 0; i < 101; i++) { + assert longMap.containsKey(i * 3331111); + } + } + + @Test + public void containsValue_Test() { + longMap.put(1000000000l, "Str"); + + assert longMap.containsValue("Str"); + } + + @Test + public void containsValue_NotOkTest() { + longMap.put(1000000000l, "Str"); + + assert !longMap.containsValue("Str1"); + assert longMap.size() == 1; + } + + @Test + public void containsValue101_Test() { + for (int i = 0; i < 101; i++) { + longMap.put((i * 3331111), "i = " + i); + } + for (int i = 0; i < 101; i++) { + assert longMap.containsKey(i * 3331111); + } + } + + @Test + public void putAll_Test() { + longMap = new LongMapImpl<>(); + longMap.put(10000000002l, "Str2"); + LongMapImpl insertLongMap = new LongMapImpl<>(); + insertLongMap.put(10000000001l, "Str"); + insertLongMap.put(10000000003l, "Str3"); + insertLongMap.put(10000000007l, "Str7"); + + assert longMap.size() == 1; + longMap.putAll(insertLongMap); + assert longMap.size() == 4; + } + + @Test + public void putAllDuplicate_Test() { + longMap = new LongMapImpl<>(); + longMap.put(10000000001l, "Str1"); + LongMapImpl insertLongMap = new LongMapImpl<>(); + insertLongMap.put(10000000001l, "Str"); + insertLongMap.put(10000000003l, "Str3"); + insertLongMap.put(10000000007l, "Str7"); + + assert longMap.size() == 1; + longMap.putAll(insertLongMap); + assert longMap.size() == 3; + } + + @Test + public void mapValueConstructor_Test() { + LongMapImpl insertLongMap = new LongMapImpl<>(); + insertLongMap.put(10000000001l, "Str"); + insertLongMap.put(10000000003l, "Str3"); + insertLongMap.put(10000000007l, "Str7"); + + longMap = new LongMapImpl<>(insertLongMap); + longMap.put(10000000002l, "Str2"); + + assert longMap.size() == 4; + assert longMap.get(10000000002l) == "Str2"; + assert longMap.get(10000000001l) == "Str"; + assert longMap.get(10000000003l) == "Str3"; + assert longMap.get(10000000007l) == "Str7"; + } + + @Test + public void mapValueConstructorDuplicate_Test() { + LongMapImpl insertLongMap = new LongMapImpl<>(); + insertLongMap.put(10000000001l, "Str"); + insertLongMap.put(10000000003l, "Str3"); + insertLongMap.put(10000000007l, "Str7"); + + longMap = new LongMapImpl<>(insertLongMap); + longMap.put(10000000003l, "Str2"); + + assert longMap.size() == 3; + assert longMap.get(10000000003l) == "Str2"; + assert longMap.get(10000000001l) == "Str"; + assert longMap.get(10000000007l) == "Str7"; + } + + @Test + public void toString_Test() { + longMap.put(10000000001l, "Str"); + longMap.put(10000000003l, "Str1"); + longMap.put(10000000007l, "Str7"); + + String result = "{10000000007=Str7, 10000000001=Str, 10000000003=Str1}"; + + assert longMap.toString().equals(result); + } + + @Test + public void toStringEmpty_Test() { + assert longMap.toString().equals("{}"); + } + + @Test + public void clear_Test() { + longMap.clear(); + assert longMap.size() == 0; + } + + @Test + public void size_Test() { + assert longMap.size() == 0; + } + + @Test + public void isEmpty_Test() { + assert longMap.isEmpty(); + } +} From 2cc8aa1f13fbc88166c42383f5a70ddc148ac0d9 Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 00:41:17 +0300 Subject: [PATCH 06/21] Updated .gitignore added target folder restriction --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a870eaf..3a0f4e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ +.target/ long-map.iml \ No newline at end of file From 187a4e9643b5591e854e39ab2882bcc23e7021dd Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 00:47:06 +0300 Subject: [PATCH 07/21] Add .circleci/config.yml --- .circleci/config.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..8ea8ffe --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,36 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/2.0/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/2.0/configuration-reference/#jobs +jobs: + # Below is the definition of your job to build and test your app, you can rename and customize it as you want. + build-and-test: + # These next lines define a Docker executor: https://circleci.com/docs/2.0/executor-types/ + # You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub. + # Be sure to update the Docker image tag below to openjdk version of your application. + # A list of available CircleCI Docker Convenience Images are available here: https://circleci.com/developer/images/image/cimg/openjdk + docker: + - image: cimg/openjdk:11.0 + # Add steps to the job + # See: https://circleci.com/docs/2.0/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + # Use mvn clean and package as the standard maven build phase + - run: + name: Build + command: mvn -B -DskipTests clean package + # Then run your tests! + - run: + name: Test + command: mvn test + +# Invoke jobs via workflows +# See: https://circleci.com/docs/2.0/configuration-reference/#workflows +workflows: + sample: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - build-and-test From 57e51faa4de8171d42a3fd120f5c73aba8c2d780 Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 00:59:13 +0300 Subject: [PATCH 08/21] Added Circle CI status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0efac4..3daa653 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # long-map -Finish development of class LongMapImpl, which implements a map with keys of type long. It has to be a hash table (like HashMap). Requirements: +# unit tests: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/Artemiy7/long-map/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Artemiy7/long-map/tree/master) From 65e8683b1a6e34867cca5460f894e31bc767aac7 Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 01:01:07 +0300 Subject: [PATCH 09/21] Added picture --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3daa653..43b6525 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # long-map -# unit tests: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/Artemiy7/long-map/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Artemiy7/long-map/tree/master) +# Unit tests: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/Artemiy7/long-map/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Artemiy7/long-map/tree/master) + +![map](https://user-images.githubusercontent.com/83453822/235795200-5703b01f-ba61-47d3-bfa3-10bd01e49282.png) From 465367bfec2b25fcf8d6727042e212cae0a25b50 Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 01:07:04 +0300 Subject: [PATCH 10/21] Updated comments removed redundant imports --- src/main/java/de/comparus/opensource/longmap/LongMap.java | 2 -- .../java/de/comparus/opensource/longmap/LongMapImpl.java | 7 ++----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMap.java b/src/main/java/de/comparus/opensource/longmap/LongMap.java index 471119b..adbf242 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMap.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMap.java @@ -1,7 +1,5 @@ package de.comparus.opensource.longmap; -import java.util.Map; - public interface LongMap { V put(long key, V value); V get(long key); diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index b2b7d6e..3b89d49 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -19,9 +19,6 @@ public LongMapImpl() { /** * Constructs a new map with the given LongMap. - * Initial capacity - 16. - * Load factor - 0.75. - * @throws NullPointerException – if the given longMap is null. */ public LongMapImpl(LongMap longMap) { this((int) (2*longMap.size()), 0.75f); @@ -45,7 +42,7 @@ public LongMapImpl(int initialCapacity, float loadFactor) { } /** - * If number of keys in the hashtable exceeds threshold + * If number of keys in the table exceeds threshold * method recalculates the index of every key in the table. */ @Override @@ -102,7 +99,7 @@ private int calculateIndex(long key) { * Resizes table and increases the capacity of table. * Recalculates the index of every key in order to make map operations more efficient. * This method is called automatically when the - * number of keys in the hashtable exceeds threshold. + * number of keys in the table exceeds threshold. */ private void resize() { int oldSize = this.table.length; From 19097d6de05ae77cafa85a90b0b7368d0f756673 Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 09:53:40 +0300 Subject: [PATCH 11/21] Updated Readme added time complexity text --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 43b6525..8e3f7c7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # long-map -# Unit tests: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/Artemiy7/long-map/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Artemiy7/long-map/tree/master) +# unit tests: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/Artemiy7/long-map/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Artemiy7/long-map/tree/master) -![map](https://user-images.githubusercontent.com/83453822/235795200-5703b01f-ba61-47d3-bfa3-10bd01e49282.png) +# time complexity text: +I added 201 entries to the LongMap using different numbers and displayed the distribution of entries in buckets on the graph. +- ![#c5f015](https://placehold.co/15x15/c5f015/c5f015.png) Average case using new Random().nextlong() +- ![#f03c15](https://placehold.co/15x15/f03c15/f03c15.png) Worst case using numbers with same number ending e.g.: 7777, 7777777, 1113337 +- ![#1589F0](https://placehold.co/15x15/1589F0/1589F0.png) Log(201) for comparison + +![map](https://user-images.githubusercontent.com/83453822/235845487-b9937785-80c7-4e14-8bd3-373083d52068.png) From c1aa81070a0c6e87cf8ff2a071c3988ad213087c Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 10:00:40 +0300 Subject: [PATCH 12/21] Updated Readme changed font size --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8e3f7c7..04ee7eb 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # long-map -# unit tests: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/Artemiy7/long-map/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Artemiy7/long-map/tree/master) +## unit tests: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/Artemiy7/long-map/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Artemiy7/long-map/tree/master) -# time complexity text: +## time complexity text: I added 201 entries to the LongMap using different numbers and displayed the distribution of entries in buckets on the graph. - ![#c5f015](https://placehold.co/15x15/c5f015/c5f015.png) Average case using new Random().nextlong() - ![#f03c15](https://placehold.co/15x15/f03c15/f03c15.png) Worst case using numbers with same number ending e.g.: 7777, 7777777, 1113337 From 1a5a084e4ced5c627add4b34e06d508341f0abe6 Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 10:10:22 +0300 Subject: [PATCH 13/21] Changed comments --- .../java/de/comparus/opensource/longmap/LongMapImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 3b89d49..5a30fae 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -43,7 +43,8 @@ public LongMapImpl(int initialCapacity, float loadFactor) { /** * If number of keys in the table exceeds threshold - * method recalculates the index of every key in the table. + * method increases the table size to (oldSize * 2) + * and recalculates the index of every key in the table. */ @Override public V put(long key, V value) { @@ -76,7 +77,7 @@ private V putEntry(LongEntry newEntry, int index) { } /** - * Copies all the entries from the longMap map to this table. + * Copies all the entries from the longMap to this table. */ public void putAll(LongMap longMap) { if (longMap != null) { @@ -96,7 +97,7 @@ private int calculateIndex(long key) { } /** - * Resizes table and increases the capacity of table. + * Resizes table and increases the capacity of table to (oldSize * 2). * Recalculates the index of every key in order to make map operations more efficient. * This method is called automatically when the * number of keys in the table exceeds threshold. From 7307ab99f4b78e6217d9fb07643d19277e23abaa Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 10:32:43 +0300 Subject: [PATCH 14/21] Updated toString() fixed bug --- .../java/de/comparus/opensource/longmap/LongMapImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 3b89d49..5bafc84 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -269,6 +269,7 @@ public boolean equals(Object o) { @Override public String toString() { + int capacity = count; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("{"); for (int i = table.length; i-- > 0;) { @@ -276,10 +277,11 @@ public String toString() { stringBuilder.append(entry.toString()); stringBuilder.append(", "); entry = entry.next; - if (i - 1 == 0 && entry == null) - return stringBuilder.replace(stringBuilder.length()-2, stringBuilder.length(), "") - .append("}").toString(); + capacity--; } + if (capacity == 0 && stringBuilder.length() > 1) + return stringBuilder.replace(stringBuilder.length()-2, stringBuilder.length(), "") + .append("}").toString(); } return "{}"; } From 2038d003c32219bfa7d1b5b550c0797a02281569 Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 10:59:53 +0300 Subject: [PATCH 15/21] Added comments to get(long key) --- src/main/java/de/comparus/opensource/longmap/LongMapImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index ec539ea..be4c010 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -132,6 +132,9 @@ private void resize() { } } + /** + * @return null if the key is not found in the table + */ @Override public V get(long key) { int index = calculateIndex(key); From f847b9263445bee36342e4a40645950b570c0f61 Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 11:03:21 +0300 Subject: [PATCH 16/21] Updated comments to get(long key) --- src/main/java/de/comparus/opensource/longmap/LongMapImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index be4c010..9cc90b9 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -133,6 +133,8 @@ private void resize() { } /** + * Searches for value by key in a table. + * @return a value if the key is present in the table * @return null if the key is not found in the table */ @Override From 5712a31086c1a7c9022cf4c959eb2a12b446a26e Mon Sep 17 00:00:00 2001 From: artemiy Date: Wed, 3 May 2023 11:20:04 +0300 Subject: [PATCH 17/21] Updated LongMapImplTest --- .../opensource/longmap/LongMapImplTest.java | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java index 4cfd56b..ef9ed3a 100644 --- a/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java +++ b/src/test/java/de/comparus/opensource/longmap/LongMapImplTest.java @@ -186,71 +186,76 @@ public void containsValue101_Test() { @Test public void putAll_Test() { longMap = new LongMapImpl<>(); - longMap.put(10000000002l, "Str2"); + longMap.put(10000000007l, "Str7"); LongMapImpl insertLongMap = new LongMapImpl<>(); - insertLongMap.put(10000000001l, "Str"); - insertLongMap.put(10000000003l, "Str3"); - insertLongMap.put(10000000007l, "Str7"); + insertLongMap.put(10000000008l, "Str8"); + insertLongMap.put(10000000009l, "Str9"); + insertLongMap.put(100000000010l, "Str10"); assert longMap.size() == 1; longMap.putAll(insertLongMap); assert longMap.size() == 4; + assert longMap.get(10000000007l).equals("Str7"); + assert longMap.get(10000000008l).equals("Str8"); + assert longMap.get(10000000009l).equals("Str9"); + assert longMap.get(100000000010l).equals("Str10"); } @Test public void putAllDuplicate_Test() { longMap = new LongMapImpl<>(); - longMap.put(10000000001l, "Str1"); + longMap.put(10000000007l, "Str7"); LongMapImpl insertLongMap = new LongMapImpl<>(); - insertLongMap.put(10000000001l, "Str"); - insertLongMap.put(10000000003l, "Str3"); - insertLongMap.put(10000000007l, "Str7"); + insertLongMap.put(10000000007l, "Str77"); + insertLongMap.put(10000000008l, "Str8"); + insertLongMap.put(10000000009l, "Str9"); assert longMap.size() == 1; longMap.putAll(insertLongMap); assert longMap.size() == 3; + assert longMap.get(10000000007l).equals("Str77"); + assert longMap.get(10000000008l).equals("Str8"); + assert longMap.get(10000000009l).equals("Str9"); } @Test public void mapValueConstructor_Test() { LongMapImpl insertLongMap = new LongMapImpl<>(); - insertLongMap.put(10000000001l, "Str"); - insertLongMap.put(10000000003l, "Str3"); insertLongMap.put(10000000007l, "Str7"); + insertLongMap.put(10000000008l, "Str8"); + insertLongMap.put(10000000009l, "Str9"); longMap = new LongMapImpl<>(insertLongMap); - longMap.put(10000000002l, "Str2"); + longMap.put(100000000010l, "Str10"); assert longMap.size() == 4; - assert longMap.get(10000000002l) == "Str2"; - assert longMap.get(10000000001l) == "Str"; - assert longMap.get(10000000003l) == "Str3"; assert longMap.get(10000000007l) == "Str7"; + assert longMap.get(10000000008l) == "Str8"; + assert longMap.get(10000000009l) == "Str9"; + assert longMap.get(100000000010l) == "Str10"; } @Test public void mapValueConstructorDuplicate_Test() { LongMapImpl insertLongMap = new LongMapImpl<>(); - insertLongMap.put(10000000001l, "Str"); - insertLongMap.put(10000000003l, "Str3"); insertLongMap.put(10000000007l, "Str7"); + insertLongMap.put(10000000008l, "Str8"); + insertLongMap.put(10000000009l, "Str9"); longMap = new LongMapImpl<>(insertLongMap); - longMap.put(10000000003l, "Str2"); + longMap.put(10000000009l, "Str99"); assert longMap.size() == 3; - assert longMap.get(10000000003l) == "Str2"; - assert longMap.get(10000000001l) == "Str"; assert longMap.get(10000000007l) == "Str7"; + assert longMap.get(10000000008l) == "Str8"; + assert longMap.get(10000000009l) == "Str99"; } @Test public void toString_Test() { longMap.put(10000000001l, "Str"); - longMap.put(10000000003l, "Str1"); - longMap.put(10000000007l, "Str7"); - String result = "{10000000007=Str7, 10000000001=Str, 10000000003=Str1}"; + String result = "{10000000001=Str}"; assert longMap.toString().equals(result); } From cb8f40b64b3ace631527f27ba04f3eb474c412bc Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 11:23:52 +0300 Subject: [PATCH 18/21] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04ee7eb..43c8b78 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## time complexity text: I added 201 entries to the LongMap using different numbers and displayed the distribution of entries in buckets on the graph. - ![#c5f015](https://placehold.co/15x15/c5f015/c5f015.png) Average case using new Random().nextlong() -- ![#f03c15](https://placehold.co/15x15/f03c15/f03c15.png) Worst case using numbers with same number ending e.g.: 7777, 7777777, 1113337 +- ![#f03c15](https://placehold.co/15x15/f03c15/f03c15.png) Worst case using numbers with same ending number e.g.: 7777, 7777777, 1113337 - ![#1589F0](https://placehold.co/15x15/1589F0/1589F0.png) Log(201) for comparison ![map](https://user-images.githubusercontent.com/83453822/235845487-b9937785-80c7-4e14-8bd3-373083d52068.png) From c6a4943a4700fd7f499886fbe6b9192a3b868e67 Mon Sep 17 00:00:00 2001 From: Artemiy7 <83453822+Artemiy7@users.noreply.github.com> Date: Wed, 3 May 2023 16:28:08 +0300 Subject: [PATCH 19/21] Update Readme, changed picture --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43c8b78..926ad3f 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ I added 201 entries to the LongMap using different numbers and displayed the dis - ![#f03c15](https://placehold.co/15x15/f03c15/f03c15.png) Worst case using numbers with same ending number e.g.: 7777, 7777777, 1113337 - ![#1589F0](https://placehold.co/15x15/1589F0/1589F0.png) Log(201) for comparison -![map](https://user-images.githubusercontent.com/83453822/235845487-b9937785-80c7-4e14-8bd3-373083d52068.png) +![map](https://user-images.githubusercontent.com/83453822/235929762-c5aa80d2-5caf-4e80-a91c-43938790efdf.png) From fe6b7a4dbaeaf190813db3b81dab129654a07c5e Mon Sep 17 00:00:00 2001 From: artemiy Date: Mon, 8 May 2023 09:10:24 +0300 Subject: [PATCH 20/21] Updated LongMapImpl remove(long key) --- .../java/de/comparus/opensource/longmap/LongMapImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 9cc90b9..5b6b17a 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -4,11 +4,13 @@ import java.util.*; public class LongMapImpl implements LongMap { + private final int DEFAULT_INITIAL_CAPACITY; private final float LOAD_FACTOR; private transient LongEntry[] table; /** The total number of entries in the map.*/ + private int count; /** The table is resized when its size exceeds this threshold. The value of this field is (int)(capacity * loadFactor)*/ private int threshold; @@ -165,11 +167,9 @@ public V remove(long key) { if (key == entry.key) { if (previous == null) { table[index] = table[index].next; - } else if (entry.next == null) { - previous.next = null; + break; } else { previous.next = entry.next; - entry = null; break; } } From 7c3fc14f20263eddb9d123122efed43b2bf53b11 Mon Sep 17 00:00:00 2001 From: artemiy Date: Mon, 8 May 2023 18:02:15 +0300 Subject: [PATCH 21/21] Updated LongMapImpl remove(long key) fixed bug --- .../opensource/longmap/LongMapImpl.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java index 5b6b17a..4a1b3d4 100644 --- a/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java +++ b/src/main/java/de/comparus/opensource/longmap/LongMapImpl.java @@ -157,27 +157,20 @@ public V remove(long key) { if (table[index] == null) return null; - V value = table[index].value; - - if (table[index].next == null) - table[index] = null; - LongEntry previous = null; for (LongEntry entry = table[index]; entry != null;) { if (key == entry.key) { - if (previous == null) { + if (previous == null) table[index] = table[index].next; - break; - } else { + else previous.next = entry.next; - break; - } + count--; + return entry.value; } previous = entry; entry = entry.next; } - count--; - return value; + return null; } @Override