diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..345e61a --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/.idea/HashTable.iml b/.idea/HashTable.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/HashTable.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..217af47 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/.idea/description.html b/.idea/description.html new file mode 100644 index 0000000..db5f129 --- /dev/null +++ b/.idea/description.html @@ -0,0 +1 @@ +Simple Java application that includes a class with main() method \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f83681f --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..61b8683 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..def6a6a --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/HashTable.iml b/HashTable.iml new file mode 100644 index 0000000..1b93dee --- /dev/null +++ b/HashTable.iml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ru/spbau/mit/kazakov/HashTable/HashTable.java b/src/ru/spbau/mit/kazakov/HashTable/HashTable.java new file mode 100644 index 0000000..9d20fd0 --- /dev/null +++ b/src/ru/spbau/mit/kazakov/HashTable/HashTable.java @@ -0,0 +1,108 @@ +package ru.spbau.mit.kazakov.HashTable; + +/** + * HashTable + * Key: String + * Value: String + * Collision resolution: Separate chaining with linked lists + */ +public class HashTable { + private int capacity = 16; + private int size = 0; + private LinkedList[] hashtable = new LinkedList[capacity]; + + /** + * Initialises all linked lists. + */ + public HashTable() { + for (int i = 0; i < capacity; i++) { + hashtable[i] = new LinkedList(); + } + } + + /** + * Returns number of keys in hashtable. + */ + public int size() { + return size; + } + + /** + * Tests if the specified string is a key in this hashtable. + */ + public boolean contains(String key) { + int hash = Math.floorMod(key.hashCode(), capacity); + return hashtable[hash].contains(key); + } + + /** + * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. + */ + public String get(String key) { + int hash = Math.floorMod(key.hashCode(), capacity); + return hashtable[hash].get(key); + } + + /** + * Maps the specified key to the specified value in this hashtable. + * Returns previous value to which the specified key was mapped, or null if this map contained no mapping for the key. + */ + public String put(String key, String value) { + int hash = Math.floorMod(key.hashCode(), capacity); + String returnValue = hashtable[hash].put(key, value); + + if (returnValue == null) { + size++; + if (size == capacity) { + rebuild(); + } + } + + return returnValue; + } + + /** + * Removes the specified key and its corresponding value from this hashtable. + * Returns removed value, or null if this map contained no mapping for the key. + */ + public String remove(String key) { + int hash = Math.floorMod(key.hashCode(), capacity); + String returnValue = hashtable[hash].remove(key); + if (returnValue != null) { + size--; + } + return returnValue; + } + + /** + * Clears this hashtable so that it contains no keys. + */ + public void clear() { + for (int i = 0; i < capacity; i++) { + hashtable[i] = new LinkedList(); + } + size = 0; + } + + /** + * Increases capacity by copying all entries in twice and rehashes all elements. + */ + private void rebuild() { + capacity *= 2; + size = 0; + LinkedList[] oldHashtable = hashtable; + + hashtable = new LinkedList[capacity]; + for (int i = 0; i < capacity; i++) { + hashtable[i] = new LinkedList(); + } + + for (int i = 0; i < capacity / 2; i++) { + while (!oldHashtable[i].empty()) { + put(oldHashtable[i].firstElementKey(), oldHashtable[i].firstElementValue()); + oldHashtable[i].remove(oldHashtable[i].firstElementKey()); + } + } + } + +} diff --git a/src/ru/spbau/mit/kazakov/HashTable/LinkedList.java b/src/ru/spbau/mit/kazakov/HashTable/LinkedList.java new file mode 100644 index 0000000..029afda --- /dev/null +++ b/src/ru/spbau/mit/kazakov/HashTable/LinkedList.java @@ -0,0 +1,135 @@ +package ru.spbau.mit.kazakov.HashTable; + +/** + * Singly linked list for hashtable. + */ +public class LinkedList { + private Node head = new Node(null, null); + + /** + * Adds new node to list, increments size. + */ + private void add(Node newNode) { + Node current = head; + + while (current.next != null) { + current = current.next; + } + + current.next = newNode; + } + + /** + * Find node with specified key in list. + * Returns null if there is no such node and found node otherwise. + */ + private Node find(String key) { + Node current = head; + + while (current.next != null) { + current = current.next; + if (current.key.equals(key)) { + return current; + } + } + + return null; + } + + /** + * Returns first element's key. + */ + public String firstElementKey() { + return head.next.key; + } + + /** + * Returns first element's value. + */ + public String firstElementValue() { + return head.next.value; + } + + /** + * Returns false if there is no elements in list and true otherwise, + */ + public boolean empty() { + return head.next == null; + } + + /** + * Returns true if list contains node with specified key and false otherwise. + */ + public boolean contains(String key) { + return find(key) != null; + } + + /** + * Returns the node's value with specified key, or null there is no such node in list. + */ + public String get(String key) { + Node foundNode = find(key); + + if (foundNode != null) { + return foundNode.value; + } else { + return null; + } + } + + /** + * Assigns specified value to node with specified key. + * Returns previous node's value or null if there was no node with specified key. + */ + public String put(String key, String value) { + Node foundNode = find(key); + + if (foundNode != null) { + String previousValue = foundNode.value; + foundNode.value = value; + return previousValue; + } else { + add(new Node(key, value)); + return null; + } + } + + /** + * Removes node with specified key from list. + * Returns removed node's value or null if there was no node with specified key. + */ + public String remove(String key) { + Node current = head; + + while (current.next != null) { + if (current.next.key.equals(key)) { + String returnValue = current.next.value; + current.next = current.next.next; + return returnValue; + } + current = current.next; + } + + return null; + } + + /** + * Node for linked list. + * Store hashtable's key and value + */ + class Node { + private Node next; + private String key, value; + + /** + * Node constructor + * + * @param key key in hash table + * @param value value in hash table + */ + Node(String key, String value) { + this.key = key; + this.value = value; + } + } +} diff --git a/test/HashTableTest.java b/test/HashTableTest.java new file mode 100644 index 0000000..43ffe10 --- /dev/null +++ b/test/HashTableTest.java @@ -0,0 +1,153 @@ +import org.junit.jupiter.api.Test; +import ru.spbau.mit.kazakov.HashTable.HashTable; + +import static org.junit.jupiter.api.Assertions.*; + +class HashTableTest { + @Test + void testConstructor() { + HashTable hashtable = new HashTable(); + } + + @Test + void testPutNew() { + HashTable hashtable = new HashTable(); + assertNull(hashtable.put("some key", "some value")); + assertEquals("some value", hashtable.get("some key")); + } + + @Test + void testPutReassign() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + assertEquals("some value", hashtable.put("some key", "another value")); + assertEquals("another value", hashtable.get("some key")); + } + + @Test + void testPutOneHundredElements() { + HashTable hashtable = new HashTable(); + for (int i = 0; i < 100; i++) { + hashtable.put(Integer.toString(i), Integer.toString(i)); + } + } + + @Test + void testContainsNotFound() { + HashTable hashtable = new HashTable(); + assertFalse(hashtable.contains("some key")); + } + + @Test + void testContainsFound() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + assertTrue(hashtable.contains("some key")); + } + + @Test + void testGetExisting() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + assertEquals("some value", hashtable.get("some key")); + } + + @Test + void testGetNotExisting() { + HashTable hashtable = new HashTable(); + assertNull(hashtable.get("some key")); + } + + + @Test + void testRemoveEmpty() { + HashTable hashtable = new HashTable(); + assertNull(hashtable.remove("some key")); + } + + @Test + void testRemoveNotExisting() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + assertNull(hashtable.remove("another key")); + } + + @Test + void testRemoveExisting() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + assertEquals("some value", hashtable.remove("some key")); + } + + @Test + void testSizeEmpty() { + HashTable hashtable = new HashTable(); + assertEquals(0, hashtable.size()); + } + + @Test + void testSizePutNew() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + assertEquals(1, hashtable.size()); + } + + @Test + void testSizePutReassign() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + hashtable.put("some key", "another value"); + assertEquals(1, hashtable.size()); + } + + @Test + void testSizeOneHundredElements() { + HashTable hashtable = new HashTable(); + for (int i = 0; i < 100; i++) { + hashtable.put(Integer.toString(i), Integer.toString(i)); + } + assertEquals(100, hashtable.size()); + } + + @Test + void testSizeRemoveNotExisting() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + hashtable.remove("another key"); + assertEquals(1, hashtable.size()); + } + + @Test + void testSizeRemoveExisting() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + hashtable.remove("some key"); + assertEquals(0, hashtable.size()); + } + + @Test + void testClearEmpty() { + HashTable hashtable = new HashTable(); + hashtable.clear(); + assertEquals(0, hashtable.size()); + } + + @Test + void testClearNotEmpty() { + HashTable hashtable = new HashTable(); + hashtable.put("some key", "some value"); + hashtable.clear(); + assertEquals(0, hashtable.size()); + } + + @Test + void testClearOneHundredElements() { + HashTable hashtable = new HashTable(); + for (int i = 0; i < 100; i++) { + hashtable.put(Integer.toString(i), Integer.toString(i)); + } + hashtable.clear(); + assertEquals(0, hashtable.size()); + } + +} \ No newline at end of file diff --git a/test/LinkedListTest.java b/test/LinkedListTest.java new file mode 100644 index 0000000..d2d3280 --- /dev/null +++ b/test/LinkedListTest.java @@ -0,0 +1,161 @@ +import org.junit.jupiter.api.Test; +import ru.spbau.mit.kazakov.HashTable.LinkedList; + +import static org.junit.jupiter.api.Assertions.*; + +class LinkedListTest { + @Test + void testConstructor() { + LinkedList list = new LinkedList(); + } + + @Test + void testPutNew() { + LinkedList list = new LinkedList(); + assertNull(list.put("some key", "some value")); + assertEquals("some value", list.get("some key")); + } + + @Test + void testPutReassign() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + assertEquals("some value", list.put("some key", "other value")); + assertEquals("other value", list.get("some key")); + } + + @Test + void testPutOneHundredElements() { + LinkedList list = new LinkedList(); + for (int i = 0; i < 100; i++) { + list.put(Integer.toString(i), Integer.toString(i)); + } + } + + @Test + void testContainsNotFound() { + LinkedList list = new LinkedList(); + assertFalse(list.contains("some key")); + } + + @Test + void testContainsFound() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + assertTrue(list.contains("some key")); + } + + @Test + void testGetExisting() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + assertEquals("some value", list.get("some key")); + } + + @Test + void testGetNotExisting() { + LinkedList list = new LinkedList(); + assertNull(list.get("some key")); + } + + + @Test + void testRemoveEmpty() { + LinkedList list = new LinkedList(); + assertNull(list.remove("some key")); + } + + @Test + void testRemoveNotExisting() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + assertNull(list.remove("another key")); + } + + @Test + void testRemoveExisting() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + assertEquals("some value", list.remove("some key")); + } + + @Test + void testFirstElementKeyOneElement() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + assertEquals("some key", list.firstElementKey()); + } + + @Test + void testFirstElementKeyAfterRemove() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + list.put("another key", "another value"); + list.remove("some key"); + assertEquals("another key", list.firstElementKey()); + } + + @Test + void testFirstElementKeyOneHundredElements() { + LinkedList list = new LinkedList(); + for (int i = 0; i < 100; i++) { + list.put(Integer.toString(i), Integer.toString(i)); + } + assertEquals("0", list.firstElementKey()); + } + + @Test + void testFirstElementValueOneElement() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + assertEquals("some value", list.firstElementValue()); + } + + @Test + void testFirstElementValueAfterRemove() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + list.put("another key", "another value"); + list.remove("some key"); + assertEquals("another value", list.firstElementValue()); + } + + @Test + void testFirstElementValueOneHundredElements() { + LinkedList list = new LinkedList(); + for (int i = 0; i < 100; i++) { + list.put(Integer.toString(i), Integer.toString(i)); + } + assertEquals("0", list.firstElementValue()); + } + + @Test + void testEmptyTrue() { + LinkedList list = new LinkedList(); + assertTrue(list.empty()); + } + + @Test + void testEmptyFalse() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + assertFalse(list.empty()); + } + + @Test + void testEmptyTrueAfterRemove() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + list.remove("some key"); + assertTrue(list.empty()); + } + + @Test + void testEmptyFalseAfterRemove() { + LinkedList list = new LinkedList(); + list.put("some key", "some value"); + list.put("another key", "another value"); + list.remove("some key"); + assertFalse(list.empty()); + } +} \ No newline at end of file