diff --git a/HashTable.iml b/HashTable.iml new file mode 100644 index 0000000..d5c0743 --- /dev/null +++ b/HashTable.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/ru/spbau/dkaznacheev/HashTable.java b/src/ru/spbau/dkaznacheev/HashTable.java new file mode 100644 index 0000000..1c7a8b7 --- /dev/null +++ b/src/ru/spbau/dkaznacheev/HashTable.java @@ -0,0 +1,150 @@ +package ru.spbau.dkaznacheev; + +/** + * Stores elements of java.lang.String inside by their hash codes. + * Automatically resizes if the size exceeds a certain fraction of capacity. + **/ +public class HashTable { + /** + * Current number of elements in the table. + **/ + private int size; + + /** + * Current capacity of the table. + **/ + private int capacity; + + /** + * The storage, consists of Lists, which contain the elements. + **/ + private List storage[]; + + /** + * The size-to-capacity ratio after which the resizing happens. + **/ + private final double SIZE_PERCENT_LIMIT = 0.75; + + /** + * Creates a HashTable with default capacity 4. + */ + public HashTable() { + this(4); + } + + /** + * Creates a HashTable with given capacity. + */ + public HashTable(int capacity) { + this.capacity = capacity; + storage = new List[capacity]; + for (int i = 0; i < capacity; i++) { + storage[i] = new List(); + } + } + + /** + * Returns the hash code of the given string. + * @param string The string that gets hashed. + * @return The hash. + **/ + private int getHash(String string) { + int stringHash = 7; + for (int i = 0; i < string.length(); i++) { + stringHash = (stringHash * 31 + string.charAt(i)); + } + return stringHash; + } + + /** + * Finds the position in the storage array by the key. + * @param key The given key + * @return The index in the array. + **/ + private int getIndex(String key) { + int hash = getHash(key); + return hash & (capacity - 1); + } + + /** + * Resizes the table. + * @param newCapacity New capacity of the table. + **/ + private void resize(int newCapacity) { + HashTable newTable = new HashTable(newCapacity); + for (int i = 0; i < capacity; i++) { + while (storage[i].size() > 0) { + List.Pair pair = storage[i].popFront(); + newTable.put(pair.getKey(), pair.getValue()); + } + } + this.storage = newTable.storage; + this.capacity = newTable.capacity; + } + + /** + * Returns how many elements is in the table. + * @return The table size. + **/ + public int size() { + return size; + } + + /** + * Checks if there is an element with the given key in the table. + * @param key The given key. + * @return Whether there is an element. + **/ + public boolean contains(String key) { + int index = getIndex(key); + return storage[index].contains(key); + } + + /** + * Changes or adds the value of the given key, returns the previous value. + * @param key The key + * @param value The new value + * @return Previous value, null if there was no value with this key. + **/ + public String put(String key, String value) { + int index = getIndex(key); + if (!storage[index].contains(key)) + size++; + String result = storage[index].put(key, value); + if (size >= SIZE_PERCENT_LIMIT * capacity) + resize(2 * capacity); + return result; + } + + /** + * Returns the value stored with given key. + * @param key The key. + * @return The value stored, null if there was no value with this key. + **/ + public String get(String key) { + int index = getIndex(key); + return storage[index].get(key); + } + + /** + * Returns the value stored with given key and removes the key-value pair from the table. + * @param key The key. + * @return The value stored, null if there was no value with this key. + **/ + public String remove(String key) { + int index = getIndex(key); + if (storage[index].contains(key)) { + size--; + } + return storage[index].remove(key); + } + + /** + * Clears the table. + **/ + public void clear() { + for (int i = 0; i < capacity; i++) + storage[i] = new List(); + size = 0; + } +} diff --git a/src/ru/spbau/dkaznacheev/HashTableTest.java b/src/ru/spbau/dkaznacheev/HashTableTest.java new file mode 100644 index 0000000..e08eb80 --- /dev/null +++ b/src/ru/spbau/dkaznacheev/HashTableTest.java @@ -0,0 +1,102 @@ +package ru.spbau.dkaznacheev; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class HashTableTest { + @Test + public void emptyHashTableSize() throws Exception { + HashTable table = new HashTable(4); + assertEquals(0, table.size()); + } + + + @Test + public void sizeChangesOnAdd() throws Exception { + HashTable table = new HashTable(4); + table.put("1", "v1"); + assertEquals(1, table.size()); + table.put("2", "v2"); + assertEquals(2, table.size()); + table.put("3", "v3"); + assertEquals(3, table.size()); + table.put("4", "v4"); + assertEquals(4, table.size()); + } + + @Test + public void containsAdded() throws Exception { + HashTable table = new HashTable(4); + table.put("1", "v1"); + assertTrue(table.contains("1")); + assertTrue(!table.contains("2")); + } + + @Test + public void containsKey() throws Exception { + HashTable table = new HashTable(4); + table.put("1", "v1"); + assertEquals(1, table.size()); + assertTrue(table.contains("1")); + assertTrue(!table.contains("v1")); + String result = table.put("1", "v11"); + assertTrue(result.equals("v1")); + assertEquals(1, table.size()); + } + + + @Test + public void getAdded() throws Exception { + HashTable table = new HashTable(4); + table.put("1", "v1"); + table.put("2", "v2"); + assertTrue(table.get("1").equals("v1")); + assertTrue(table.get("2").equals("v2")); + } + + @Test + public void getNotExisting() throws Exception { + HashTable table = new HashTable(4); + table.put("1", "v1"); + table.put("2", "v2"); + assertEquals(null, table.get("3")); + } + + @Test + public void removeChangesSize() throws Exception { + HashTable table = new HashTable(4); + table.put("1", "v1"); + table.put("2", "v2"); + table.remove("1"); + assertEquals(1, table.size()); + table.remove("3"); + assertEquals(1, table.size()); + } + + @Test + public void removedElements() throws Exception { + HashTable table = new HashTable(4); + table.put("1", "v1"); + table.put("2", "v2"); + String result = table.remove("1"); + assertTrue(result.equals("v1")); + assertTrue(!table.contains("1")); + assertTrue(table.contains("2")); + result = table.remove("3"); + assertNull(result); + } + + @Test + public void clearTable() throws Exception { + HashTable table = new HashTable(4); + table.put("1", "v1"); + table.put("2", "v2"); + table.clear(); + assert(table.size() == 0); + assert(!table.contains("1")); + assert(!table.contains("2")); + } + +} \ No newline at end of file diff --git a/src/ru/spbau/dkaznacheev/List.java b/src/ru/spbau/dkaznacheev/List.java new file mode 100644 index 0000000..68d4f07 --- /dev/null +++ b/src/ru/spbau/dkaznacheev/List.java @@ -0,0 +1,222 @@ +package ru.spbau.dkaznacheev; + +import com.sun.org.apache.bcel.internal.generic.LUSHR; + +/** + * Linked list that stores elements of java.lang.String. + **/ +public class List { + + /** + * Subclass that describes the key-value pair. + */ + public static class Pair { + + /** + * Key of pair + */ + private String key; + + /** + * Value of pair + */ + private String value; + + /** + * Creates key-value pair + * @param key key of pair + * @param value value of pair + */ + public Pair(String key, String value) { + this.key = key; + this.value = value; + } + + /** + * Returns key + * @return key + */ + public String getKey() { + return key; + } + + /** + * Returns value + * @return value + */ + public String getValue() { + return value; + } + + /** + * Sets key + * @param key the new key + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Sets value + * @param value the new value + */ + public void setValue(String value) { + this.value = value; + } + + } + + /** + * Node Subclass that describes the list's node. + **/ + private class Node { + + /** + * The key-value pair inside the node. + */ + private Pair pair; + + /** + * The next node. + **/ + private Node next; + + /** + * Creates a node with key and value + * @param key key of node + * @param value value of node + */ + private Node (String key, String value) { + this.pair = new Pair(key, value); + this.next = null; + } + } + + /** + * The first element of the list. + **/ + private Node head; + + /** + * Size of the list. + **/ + private int size; + + /** + * Creates empty list + */ + public List() { } + + /** + * Creates List with one node + * @param key key of node + * @param value value of node + */ + public List (String key, String value) { + head = new Node(key, value); + head.next = null; + size = 1; + } + + /** + * Returns the size of list. + * @return size of list + **/ + public int size() { + return size; + } + + /** + * Returns the value of the node with given key. + * @param key The key. + * @return The value, null if there is no such key. + **/ + public String get (String key) { + if (head == null) + return null; + Node node = head; + do { + if (node.pair.key.equals(key)) + return node.pair.value; + node = node.next; + } while (node != null); + return null; + } + + /** + * Changes or adds the value of the given key, returns the previous value. + * @param key The key + * @param value The new value + * @return Previous value, null if there was no value with this key. + **/ + public String put (String key, String value) { + if (head == null) { + head = new Node(key, value); + size++; + return null; + } + Node node = head; + Node tail; + do { + if (node.pair.key.equals(key)) { + String oldValue = node.pair.value; + node.pair.value = value; + return oldValue; + } + tail = node; + node = node.next; + } while (node != null); + tail.next = new Node(key, value); + size++; + return null; + } + + /** + * Returns the first key-value pair of the list, and then deletes it. + * @return First node, or null if the list is empty. + **/ + public Pair popFront () { + if (head == null) + return null; + Pair result = head.pair; + head = head.next; + size--; + return result; + } + + /** + * Returns the value stored with given key and removes the key-value pair from the list. + * @param key The key. + * @return The value stored, null if there was no value with this key. + **/ + public String remove (String key) { + if (head == null) + return null; + Node node = head; + Node prev = null; + do { + if (node.pair.key.equals(key)) { + String oldValue = node.pair.value; + if (prev != null) { + prev.next = node.next; + } else { + head = node.next; + } + size--; + return oldValue; + } + prev = node; + node = node.next; + } while (node != null); + return null; + } + + /** + * Checks if there is an element with the given key in the list. + * @param key The given key. + * @return Whether there is an element. + **/ + public boolean contains (String key) { + return (get(key) != null); + } +} diff --git a/src/ru/spbau/dkaznacheev/ListTest.java b/src/ru/spbau/dkaznacheev/ListTest.java new file mode 100644 index 0000000..934c0fd --- /dev/null +++ b/src/ru/spbau/dkaznacheev/ListTest.java @@ -0,0 +1,88 @@ +package ru.spbau.dkaznacheev; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ListTest { + @Test + public void sizeChangesOnAdd() throws Exception { + List list = new List(); + assertEquals (0, list.size()); + list.put("1", "v1"); + assertEquals (1, list.size()); + list.put("2", "v2"); + assertEquals (2, list.size()); + list.put("2", "v3"); + assertEquals (2, list.size()); + } + + @Test + public void getTest() throws Exception { + List list = new List("1", "v1"); + assertTrue(list.get("1").equals("v1")); + assertNull(list.get("2")); + } + + @Test + public void putTest() throws Exception { + List list = new List(); + list.put("1", "v1"); + assertEquals(1, list.size()); + assertTrue(list.contains("1")); + assertTrue(!list.contains("v1")); + String result = list.put("1", "v11"); + assertTrue(result.equals("v1")); + assertEquals(1, list.size()); + } + + @Test + public void popFrontTest() throws Exception { + List list = new List(); + list.put("1", "v1"); + list.put("2", "v2"); + List.Pair pair = list.popFront(); + assertTrue(pair.getKey().equals("1")); + assertTrue(pair.getValue().equals("v1")); + assertEquals(1, list.size()); + assertTrue(!list.contains("1")); + pair = list.popFront(); + assertTrue(pair.getKey().equals("2")); + assertTrue(pair.getValue().equals("v2")); + assertEquals(0, list.size()); + pair = list.popFront(); + assertNull(pair); + } + + @Test + public void removeTest() throws Exception { + List list = new List(); + list.put("1", "v1"); + list.put("2", "v2"); + list.put("3", "v3"); + String result = list.remove("2"); + assertTrue(result.equals("v2")); + assertTrue(!list.contains("2")); + assertEquals(2, list.size()); + result = list.remove("4"); + assertNull(result); + assertEquals(2, list.size()); + list.remove("1"); + list.remove("3"); + assertEquals(0, list.size()); + result = list.remove("1"); + assertNull(result); + assertEquals(0, list.size()); + } + + @Test + public void containsAdded() throws Exception { + List list = new List(); + list.put("1", "v1"); + list.put("2", "v2"); + assertTrue(list.contains("1")); + assertTrue(!list.contains("3")); + } + + +} \ No newline at end of file diff --git a/src/ru/spbau/dkaznacheev/Main.java b/src/ru/spbau/dkaznacheev/Main.java new file mode 100644 index 0000000..4a0dbf8 --- /dev/null +++ b/src/ru/spbau/dkaznacheev/Main.java @@ -0,0 +1,46 @@ +package ru.spbau.dkaznacheev; + +public class Main { + static void test1() { + HashTable hashTable = new HashTable(); + hashTable.put("Vasya", "1 group"); + hashTable.put("Petya", "2 group"); + hashTable.put("Igor", "3 group"); + System.out.println(hashTable.get("Vasya")); + System.out.println(hashTable.get("Petya")); + System.out.println(hashTable.put("Igor", "4 group")); + System.out.println(hashTable.get("Igor")); + System.out.println(hashTable.get("Oleg")); + } + + static void test2 () { + List list = new List("ka", "va"); + System.out.println(list.get("ka")); + System.out.println(list.put("ka", "v1")); + System.out.println(list.contains("ka")); + + list.put("kb", "vb"); + System.out.println(list.get("kb")); + list.remove("ka"); + System.out.println(list.get("ka")); + } + + static void test3 () { + HashTable table = new HashTable(4); + table.put("ccc", "0"); + table.put("ddd", "1"); + table.put("aaa", "2"); + table.put("bbb", "3"); + System.out.println(table.get("aaa")); + System.out.println(table.get("bbb")); + System.out.println(table.get("ccc")); + System.out.println(table.get("ddd")); + + } + + public static void main(String[] args) { + test1(); + test2(); + test3(); + } +}