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