From 7f8273d049468b55873fb48012b6770f2620b780 Mon Sep 17 00:00:00 2001 From: norangebit Date: Tue, 30 Apr 2019 19:55:47 +0200 Subject: [PATCH] add hash table --- .../datastructures/dictionary/HashTable.kt | 123 ++++++++++++++++++ .../dictionary/HashTableTest.kt | 80 ++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/HashTable.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/datastructures/dictionary/HashTableTest.kt diff --git a/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/HashTable.kt b/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/HashTable.kt new file mode 100644 index 0000000..ba42e62 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/HashTable.kt @@ -0,0 +1,123 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.datastructures.dictionary + +import arrow.core.None +import arrow.core.Option +import arrow.core.Some +import arrow.core.toOption + +class HashTable : Dictionary { + private val array = Array>>(HASHTABLE_SIZE) { None } + private var size = 0 + + override fun get(key: K): Option { + return get(array[getIndex(key)], key) + .map { it.value } + } + + private fun get(node: Option>, key: K): Option> { + return node.flatMap { + when (key) { + it.key -> node + else -> get(it.next, key) + } + } + } + + override fun set(key: K, value: V) { + val index = getIndex(key) + + array[index] = set( + array[index], + key, + value + ).toOption() + } + + private fun set( + node: Option>, + key: K, + value: V + ): Node { + return node.fold( + { + size++ + Node(key, value) + }, + { + when (it.key) { + key -> it.clone(value = value) + else -> it.clone(next = set(it.next, key, value).toOption()) + } + } + ) + } + + override fun delete(key: K) { + val index = getIndex(key) + + array[index] = delete(array[index], key) + } + + private fun delete(node: Option>, key: K): Option> { + return node.flatMap { + when (it.key) { + key -> { + size-- + it.next + } + else -> it.clone(next = delete(it.next, key)).toOption() + } + } + } + + override fun contains(key: K): Boolean = get(key) is Some + + override fun isEmpty(): Boolean = size == 0 + + override fun size(): Int = size + + private fun getIndex(key: K): Int { + return key.hashCode() % HASHTABLE_SIZE + } + + data class Node( + val key: K, + val value: V, + val next: Option> = None + ) { + fun clone( + key: K = this.key, + value: V = this.value, + next: Option> = this.next + ) = Node(key, value, next) + } + + companion object { + const val HASHTABLE_SIZE = 40 + } +} \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/datastructures/dictionary/HashTableTest.kt b/src/test/kotlin/it/norangeb/algorithms/datastructures/dictionary/HashTableTest.kt new file mode 100644 index 0000000..b7bfc48 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/datastructures/dictionary/HashTableTest.kt @@ -0,0 +1,80 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.datastructures.dictionary + +import arrow.core.None +import arrow.core.Some +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.shouldEqual +import org.junit.jupiter.api.Test + +class HashTableTest { + + @Test + fun test() { + val map = HashTable() + + map.size() `should be equal to` 0 + map.isEmpty() `should be equal to` true + map.contains(3) `should be equal to` false + + map[1] = "Zero" + map[4] = "Quattro" + + map.contains(1) `should be equal to` true + map[1] shouldEqual Some("Zero") + map.size() `should be equal to` 2 + map.isEmpty() `should be equal to` false + map.delete(5) + + map[1] = "Uno" + + map[1] shouldEqual Some("Uno") + + map[41] = "41" + map[81] = "81" + map[121] = "121" + + map.contains(41) `should be equal to` true + map.size() `should be equal to` 5 + map[1] shouldEqual Some("Uno") + map[81] shouldEqual Some("81") + map[121] shouldEqual Some("121") + + map.delete(41) + + map.size() `should be equal to` 4 + map.contains(41) `should be equal to` false + + map.delete(121) + + map.size() `should be equal to` 3 + map.contains(121) `should be equal to` false + map.contains(81) `should be equal to` true + map[1] shouldEqual Some("Uno") + map[41] shouldEqual None + } +} \ No newline at end of file