diff --git a/hw1/.idea/misc.xml b/hw1/.idea/misc.xml new file mode 100644 index 0000000..0548357 --- /dev/null +++ b/hw1/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hw1/.idea/modules.xml b/hw1/.idea/modules.xml new file mode 100644 index 0000000..9b27cd1 --- /dev/null +++ b/hw1/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/hw1/hw1.iml b/hw1/hw1.iml new file mode 100644 index 0000000..bf5ed58 --- /dev/null +++ b/hw1/hw1.iml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw1/ru/spbau/solikov/hw1/src/HashTable.java b/hw1/ru/spbau/solikov/hw1/src/HashTable.java new file mode 100644 index 0000000..f71fafd --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/src/HashTable.java @@ -0,0 +1,157 @@ +package ru.spbau.solikov.hw1.src; + +/** + * Implementation of hash table structure using separate chaining + */ +public class HashTable { + + /** + * Number used in hash function + */ + private static final int DEFAULT_HASH_PRIME_NUMBER = 31; + + private List[] table; + private int capacity = 2; + private int size; + + /** + * Creates a HashTable of default capacity + */ + public HashTable() { + table = new List[2]; + size = 0; + } + + /** + * Returns number of keys + * + * @return size + */ + public int size() { + return size; + } + + + /** + * Iterates on each list in HashTable and clears it, capacity is the same + */ + public void clear() { + for (List list : table) { + if (list != null) { + list.clear(); + } + } + size = 0; + } + + /** + * Hash function for obtaining a code from key + * + * @param key + * @return hash code + */ + private int getHash(String key) { + int hash = 0; + for (int i = 0; i < key.length(); i++) { + hash = (DEFAULT_HASH_PRIME_NUMBER * hash + key.charAt(i)) % capacity; + } + return hash; + } + + /** + * Returns data stored by key or null if not found + * + * @param key + * @return data or null if not found + */ + public String get(String key) { + int hash = getHash(key); + if (table[hash] == null){ + return null; + } + return table[hash].find(key); + } + + /** + * Increases the capacity of HashTable by 2 times + */ + private void increase() { + List[] oldTable = table; + table = new List[2 * capacity]; + capacity *= 2; + + for (List list : oldTable) { + if (list != null) { + while (!list.isEmpty()) { + String key = list.getHeadsKey(); + int hash = getHash(key); + if (table[hash] == null) { + table[hash] = new List(); + } + table[hash].insert(key, list.getHeadsData()); + list.delete(key); + } + } + } + } + + /** + * Allows to add the pair of key and data into HashTable, if there's not enough space calls increase() + * + * @param key + * @param data + * @return data that was stored before by the same key or null if this space was empty + */ + public String put(String key, String data) { + int hash = getHash(key); + if (table[hash] == null) { + table[hash] = new List(); + } + + List current = table[hash]; + String deleted = current.delete(key); + current.insert(key, data); + + if (deleted == null) { + size++; + } + + if (size >= capacity) { + increase(); + } + + return deleted; + } + + /** + * Allows to remove data stored by key + * + * @param key + * @return data stored by key + */ + public String remove(String key) { + int hash = getHash(key); + + if (table[hash] == null) { + return null; + } + + String deleted = table[hash].delete(key); + if (deleted != null) { + size--; + return deleted; + } + + return null; + } + + /** + * Checks whether HashTable contains some data by passed key + * + * @param key + * @return data or null if the HashTable doesn't contain key + */ + public boolean contains(String key) { + return get(key) != null; + } +} diff --git a/hw1/ru/spbau/solikov/hw1/src/List.java b/hw1/ru/spbau/solikov/hw1/src/List.java new file mode 100644 index 0000000..04f8d48 --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/src/List.java @@ -0,0 +1,121 @@ +package ru.spbau.solikov.hw1.src; + +/** + * Implementation of single-linked list that stores Nodes of keys and data + */ +public class List { + + /** + * Class that stores keys and data and has link for next Node + */ + private class Node { + + private Node next; + + private String key; + + private String data; + + public Node(Node n, String k, String d) { + next = n; + key = k; + data = d; + } + } + + private Node head; + + private Node tail; + + public Node getHead() { + return head; + } + + /** + * Adds a pair of key and data in List + * + * @param key + * @param data + */ + public void insert(String key, String data) { + if (head != null) { + tail.next = new Node(null, key, data); + tail = tail.next; + } else { + tail = head = new Node(null, key, data); + } + } + + /** + * Checks if the list does not contain any elements + * + * @return boolean - true if the list is empty + */ + public boolean isEmpty() { + return head == null; + } + + /** + * Searches for data by key in List + * + * @param key + * @return data or null if not found + */ + public String find(String key) { + Node current = head; + while (current != null) { + if (current.key.equals(key)) { + return current.data; + } + current = current.next; + } + + return null; + } + + public String getHeadsKey() { + return head.key; + } + + public String getHeadsData() { + return head.data; + } + + /** + * Deletes a data stored by key in List + * + * @param key + * @return data that was deleted + */ + public String delete(String key) { + if (head == null) { + return null; + } + + if (head.key.equals(key)) { + String deleted = head.data; + head = head.next; + return deleted; + } + + Node current = head; + while (current.next != null) { + if (current.next.key.equals(key)) { + String deleted = current.next.data; + current.next = current.next.next; + return deleted; + } + current = current.next; + } + + return null; + } + + /** + * Clears the List + */ + public void clear() { + head = null; + tail = null; + } +} diff --git a/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java b/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java new file mode 100644 index 0000000..6a881ca --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java @@ -0,0 +1,41 @@ +package ru.spbau.solikov.hw1.src.primitive.test; + +import ru.spbau.solikov.hw1.src.HashTable; + +/** + * Class for testing all the HashTable functions + */ + +public class HashTableTest { + public static void main(String[] args) { + HashTable hashTable = new HashTable(); + + for (int i = 0; i < 100; i++){ + hashTable.put(String.valueOf(i), String.valueOf(i * i)); + } + + System.out.println(hashTable.size() + " - size of hashtable (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.get("10") + " - square of 10 (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.put("10", "200") + " - changed square of 10 (must have been 100)"); + + System.out.println(); + + System.out.println(hashTable.contains("10") + " - contains '10' (must be true)"); + + System.out.println(); + + System.out.println(hashTable.remove("10") + " - removed '10'"); + + System.out.println(); + + hashTable.clear(); + + System.out.println(hashTable.size() + " - size of hashtable (must be 0)"); + } +} diff --git a/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java new file mode 100644 index 0000000..be54d90 --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java @@ -0,0 +1,29 @@ +package ru.spbau.solikov.hw1.src.primitive.test; + +import ru.spbau.solikov.hw1.src.List; + +/** + * Class for testing all the List functions + */ + +public class ListTest { + public static void main(String[] args) { + List l = new List(); + + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + + System.out.println(); + + System.out.println(l.find("5") + " - found key '5'."); + l.delete("5"); + System.out.println(l.delete("5") + " - deleted key '5'."); + + System.out.println(); + + l.clear(); + System.out.print(l.getHead() == null); + System.out.println(" - cleared the list."); + } +} diff --git a/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java b/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java new file mode 100644 index 0000000..958c53d --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java @@ -0,0 +1,41 @@ +package ru.spbau.solikov.hw1.src.primitive.test; + +import ru.spbau.solikov.hw1.src.HashTable; + +/** + * Class for testing all the HashTable functions + */ + +public class HashTableTest { + public static void main(String[] args) { + HashTable hashTable = new HashTable(); + + for (int i = 0; i < 100; i++){ + hashTable.put(String.valueOf(i), String.valueOf(i * i)); + } + + System.out.println(hashTable.size() + " - size of hashtable (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.get("10") + " - square of 10 (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.put("10", "200") + " - changed square of 10 (must have been 100)"); + + System.out.println(); + + System.out.println(hashTable.contains("10") + " - contains '10' (must be true)"); + + System.out.println(); + + System.out.println(hashTable.remove("10") + " - removed '10'"); + + System.out.println(); + + hashTable.clear(); + + System.out.println(hashTable.size() + " - size of hashtable (must be 0)"); + } +} \ No newline at end of file diff --git a/hw1/ru/spbau/solikov/hw1/test/HashTableUnitTest.java b/hw1/ru/spbau/solikov/hw1/test/HashTableUnitTest.java new file mode 100644 index 0000000..f7d5d17 --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/test/HashTableUnitTest.java @@ -0,0 +1,253 @@ +package ru.spbau.solikov.hw1.src.primitive.test; + +import org.junit.Before; +import org.junit.Test; +import ru.spbau.solikov.hw1.src.HashTable; + +import static org.junit.Assert.*; + +/** + * Test class for HashTable with separate chaining + */ +public class HashTableTest { + + private HashTable table; + + /** + * Constructs new hash table for every test + */ + @Before + public void setUp() { + table = new HashTable(); + } + + /** + * Tests if the size of empty table is 0 + */ + @Test + public void testSizeOfEmptyHashTable() { + assertEquals(0, table.size()); + } + + /** + * Tests if 'put' puts the right key + */ + @Test + public void testPut() { + table.put("27", "09"); + assertEquals(true, table.contains("27")); + } + + /** + * Tests if the size after many 'puts' is correct + */ + @Test + public void testSizeAfterPut() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals(10, table.size()); + } + + /** + * Tests if the size after 'put' in the same key is correct + */ + @Test + public void testSizeAfterPutExistingKey() { + table.put("123", "321"); + table.put("123", "333"); + assertEquals(1, table.size()); + } + + /** + * Tests if 'put' inserts new value instead of previous + */ + @Test + public void testPutExistingKey() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.put("4", "99"); + assertEquals("99", table.get("4")); + } + + /** + * Tests if 'size' on empty table is 0 + */ + @Test + public void testClearEmpty() { + table.clear(); + assertEquals(0, table.size()); + } + + /** + * Tests if 'clear' works right on table with many keys + */ + @Test + public void testClear() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.clear(); + assertEquals(0, table.size()); + } + + /** + * Tests if 'get' from empty table returns null + */ + @Test + public void testGetFromEmpty() { + assertEquals(null, table.get("Deadline")); + } + + /** + * Tests if 'get' from one-element table returns the right value + */ + @Test + public void testGetElementFromOneElementTable() { + table.put("1", "2"); + assertEquals("2", table.get("1")); + } + + /** + * Tests if 'get' on many-element table returns the right value + */ + @Test + public void testGetElementFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals("5", table.get("5")); + } + + /** + * Tests if 'get' does not change the size of hash table + */ + @Test + public void testGetDoesNotChangeSize() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.get("5"); + assertEquals(10, table.size()); + } + + /** + * Tests if 'remove' does nothing with table after removing from empty table + */ + @Test + public void testSizeAfterRemovingFromEmptyTable() { + table.remove("123"); + assertEquals(0, table.size()); + } + + /** + * Tests if 'remove' on empty table returns null + */ + @Test + public void testRemoveFromEmptyTable() { + assertEquals(null, table.remove("123")); + } + + /** + * Tests if 'remove' on one-element table returns the right value + */ + @Test + public void testRemoveFromOneElementTable() { + table.put("123", "321"); + assertEquals("321", table.get("123")); + } + + /** + * Tests if the size of table becomes zero after removing from one-element table + */ + @Test + public void testSizeAfterRemovingFromOneElementTable() { + table.put("123", "321"); + table.remove("123"); + assertEquals(0, table.size()); + } + + /** + * Tests if the size of hash table is not affected by removing element that does not exist + */ + @Test + public void testSizeAfterRemovingNonexistingFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.remove("100"); + assertEquals(10, table.size()); + } + + /** + * Tests if size after removing existing element decreases + */ + @Test + public void testSizeAfterRemoveFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.remove("5"); + assertEquals(9, table.size()); + } + + /** + * Tests if 'remove' from many-element table returns the right value + */ + @Test + public void testRemoveFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals("4", table.remove("4")); + } + + /** + * Tests if 'contains' returns false on empty table + */ + @Test + public void testContainsFromEmptyTable() { + assertEquals(false, table.contains("some")); + } + + /** + * Tests if 'contains' returns true on element that exists in one-element table + */ + @Test + public void testContainsFromOneElementTable() { + table.put("Wednesday", "27.09"); + assertEquals(true, table.contains("Wednesday")); + } + + /** + * Tests if 'contains' returns false on element that does not exist in one-element table + */ + @Test + public void testContainsNonexistingElementFromOneElementTable() { + table.put("Wednesday", "27.09"); + assertEquals(false, table.contains("Sunday")); + } + + /** + * Tests if 'contains' returns true on many-element table + */ + @Test + public void testContainsManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals(true, table.contains("4")); + } + + /** + * Tests if 'contains' returns false on elements that don't exist in many-element table + */ + @Test + public void testContainsNonexistingElementFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals(false, table.contains("123")); + } +} \ No newline at end of file diff --git a/hw1/ru/spbau/solikov/hw1/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/test/ListTest.java new file mode 100644 index 0000000..b3f6b75 --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/test/ListTest.java @@ -0,0 +1,29 @@ +package ru.spbau.solikov.hw1.src.primitive.test; + +import ru.spbau.solikov.hw1.src.List; + +/** + * Class for testing all the List functions + */ + +public class ListTest { + public static void main(String[] args) { + List l = new List(); + + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + + System.out.println(); + + System.out.println(l.find("5") + " - found key '5'."); + l.delete("5"); + System.out.println(l.delete("5") + " - deleted key '5'."); + + System.out.println(); + + l.clear(); + System.out.print(l.getHead() == null); + System.out.println(" - cleared the list."); + } +} \ No newline at end of file diff --git a/hw1/ru/spbau/solikov/hw1/test/ListUnitTest.java b/hw1/ru/spbau/solikov/hw1/test/ListUnitTest.java new file mode 100644 index 0000000..db8c8f1 --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/test/ListUnitTest.java @@ -0,0 +1,127 @@ +package ru.spbau.solikov.hw1.src.primitive.test; + +import org.junit.Before; +import org.junit.Test; +import ru.spbau.solikov.hw1.src.List; + +import static org.junit.Assert.*; + +/** + * Test class for class List that implements single-linked list + */ +public class ListTest { + + private List l; + + /** + * Constructs new list for every test + */ + @Before + public void setUp() { + l = new List(); + } + + /** + * Tests if 'isEmpty' on empty list returns true + */ + @Test + public void testIsEmptyOfEmptyList() { + assertEquals(true, l.isEmpty()); + } + + /** + * Tests if 'insert' on empty list inserts the right data + */ + @Test + public void testInsert() { + l.insert("SPbAU", "Java"); + assertEquals("Java", l.getHeadsData()); + } + + /** + * Tests if many 'insert's insert the right data + */ + @Test + public void testManyInserts() { + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + + int number = 0; + while (!l.isEmpty()) { + assertEquals(Integer.toString(number), l.getHeadsData()); + number++; + l.delete(l.getHeadsKey()); + } + } + + /** + * Tests if 'find' on empty list returns null + */ + @Test + public void testFindNonexistingElement() { + assertEquals(null, l.find("Something")); + } + + /** + * Tests if 'find' on one-element list returns right value + */ + @Test + public void testFindExistingElement() { + l.insert("SPbAU", "Java"); + assertEquals("Java", l.find("SPbAU")); + } + + /** + * Tests if 'find' on many-element list returns right value + */ + @Test + public void testFindExistingElementManyElements() { + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + l.insert("SPbAU", "Java"); + assertEquals("Java", l.find("SPbAU")); + } + + /** + * Tests if 'delete' on empty list does nothing + */ + @Test + public void testDeleteElementFromEmptyList() { + l.delete("SomeKey"); + assertEquals(true, l.isEmpty()); + } + + /** + * Tests if 'delete' nonexisting element on one-element list does nothing + */ + @Test + public void testDeleteNonexistingElement() { + l.insert("1", "2"); + l.delete("5"); + assertNotEquals(true, l.isEmpty()); + } + + /** + * Tests if 'delete' from one-element list truly deletes element + */ + @Test + public void testDeleteExistingElement() { + l.insert("SPbAU", "Java"); + l.delete("SPbAU"); + assertEquals(true, l.isEmpty()); + } + + /** + * Tests if 'clear' deletes all elements from many-element list + */ + @Test + public void testClear() { + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + l.clear(); + assertEquals(true, l.isEmpty()); + } +} \ No newline at end of file