This commit is contained in:
2026-03-13 12:11:24 +00:00
commit fb57e2e831
763 changed files with 12954 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
package collections;
public class ImperialPair<S, T> {
private final S first;
private final T second;
public ImperialPair(S first, T second) {
this.first = first;
this.second = second;
}
public S getFirst() {
return first;
}
public T getSecond() {
return second;
}
}

View File

@@ -0,0 +1,198 @@
package collections;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.NoSuchElementException;
public final class SinglyLinkedListJava<T> implements ImperialMutableList<T> {
private static class Node<T> {
T element;
Node<T> next;
Node(T element, Node<T> next) {
this.element = element;
this.next = next;
}
Node(T element) {
this.element = element;
this.next = null;
}
}
private int size = 0;
private Node<T> head = null;
@Override
public int getSize() {
return size;
}
@Override
public String toString() {
final StringBuilder result = new StringBuilder();
result.append("[");
Node<T> current = head;
boolean first = true;
while (current != null) {
if (!first) {
result.append(", ");
}
first = false;
result.append(current.element);
current = current.next;
}
result.append("]");
return result.toString();
}
@NotNull
@Override
public T get(int index) {
checkIndexInBounds(index);
Node<T> current = head;
for (int i = 1; i<index; i++) {
current = current.next;
}
return current.element;
}
@Override
public void add(int index, T element) {
checkIndexInBounds(index, true);
size++;
var pair = traverseTo(index);
Node<T> newNode = new Node<>(element, pair.getSecond());
if (pair.getFirst() == null) {
head = newNode;
} else {
pair.getSecond().next = newNode;
}
}
@Override
public void clear() {
head = null;
size = 0;
}
@Override
public boolean contains(T element) {
var current = head;
while (current != null) {
if (current.element == element) {
return true;
}
current = current.next;
}
return false;
}
@Override
public T removeAt(int index) {
checkIndexInBounds(index);
var pair = traverseTo(index);
var result = pair.getSecond().element;
unlink(pair.getFirst(), pair.getSecond());
return result;
}
@Override
public boolean remove(T element) {
Node<T> previous = null;
Node<T> current = head;
while (current != null) {
if (current.element == element) {
unlink(previous, current);
return true;
}
previous = current;
current = current.next;
}
return false;
}
@Override
public T set(int index, T element) {
checkIndexInBounds(index);
Node<T> current = head;
for (int i = 0;i<index;i++) {
current = current.next;
}
var result = current.element;
current.element = element;
return result;
}
@Override
public void addAll(int index, @NotNull ImperialMutableList<T> other) {
checkIndexInBounds(index, true);
val iterator = other.iterator();
val start = Node(iterator.next());
var end = start;
while (iterator.hasNext()) {
end.next = Node(iterator.next());
end = end.next!!;
}
if (index == 0) {
end.next = head;
head = start;
} else {
val (previous, current) = traverseTo(index);
assert(previous != null);
previous!!.next = start;
end.next = current;
}
size += other.size;
}
@NotNull
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
// TODO: you need to populate this anonymous class, including implementing its methods.
@Override
public boolean hasNext() {
throw new RuntimeException("TODO: you need to implement this method.");
}
@Override
public T next() {
throw new RuntimeException("TODO: you need to implement this method.");
}
};
}
private void checkIndexInBounds(int index, boolean inclusive) {
if (index < 0 || index >= (inclusive ? size + 1 : size)) {
throw new IndexOutOfBoundsException();
}
}
private void checkIndexInBounds(int index) {
checkIndexInBounds(index, false);
}
private ImperialPair<Node<T>, Node<T>> traverseTo(int index) {
Node<T> previous = null;
Node<T> current = head;
for (int i = 0; i < index; i++) {
previous = current;
current = current.next;
}
return new ImperialPair<>(previous, current);
}
private void unlink(Node<T> previous, Node<T> current) {
if (previous == null) {
head = current.next;
} else {
previous.next = current.next;
}
size--;
}
}

View File

@@ -0,0 +1,84 @@
package collections
class Hashmap<K, V>(
private val bucketFactory: () -> Bucket<K, V>,
) : ImperialMutableMap<K, V> {
// Problem: TODO - briefly describe the problem you have identified.
// Solution: TODO - briefly explain how you have solved this problem.
//
// Problem: TODO - briefly describe the problem you have identified.
// Solution: TODO - briefly explain how you have solved this problem.
//
// TODO - add further Problem/Solution entries for other problems that you identify.
var buckets: Array<Bucket<K, V>> = Array(16) { bucketFactory() }
override var size: Int = 0
// The iterator implementation works by putting all entries of the map into a list, and then returns an iterator to
// this list. This is simpler than the "on demand" iterator that you implemented during a lab exercise on hashmaps.
// This simpler approach is intentional here; it is not one of the problems you are supposed to identify.
override fun iterator(): Iterator<ImperialMutableMap.Entry<K, V>> = helperMethod().iterator()
override fun put(key: K, value: V): V? {
val bucket = key.bucket()
for (i in 0..<bucket.size) {
val entry = bucket[i]
if (entry.key == key) {
val result = entry.value
entry.value = value
if (size > buckets.size * MAX_LOAD_FACTOR) {
resize()
}
return result
}
}
bucket.add(0, ImperialMutableMap.Entry(key, value))
size++
return null
}
override fun get(key: K): V? {
for (entry in key.bucket()) {
if (entry.key == key) {
return entry.value
}
}
return null
}
override fun remove(key: K): V? {
val bucket = key.bucket()
for ((index, entry) in bucket.withIndex()) {
if (entry.key == key) {
val result = entry.value
bucket.removeAt(index)
return result
}
}
return null
}
fun K.bucketIndex(): Int = Math.floorMod(hashCode(), buckets.size)
fun K.bucket(): Bucket<K, V> = buckets[bucketIndex()]
fun resize() {
val entries = helperMethod()
buckets = Array(buckets.size * 2) { bucketFactory() }
entries.forEach {
put(it.key, it.value)
}
}
fun helperMethod(): SinglyLinkedList<ImperialMutableMap.Entry<K, V>> {
val entries = SinglyLinkedList<ImperialMutableMap.Entry<K, V>>()
for (bucket in buckets) {
for (entry in bucket) {
entries.add(0, entry)
}
}
return entries
}
}

View File

@@ -0,0 +1,5 @@
package collections
typealias Bucket<K, V> = ImperialMutableList<ImperialMutableMap.Entry<K, V>>
const val MAX_LOAD_FACTOR = 0.75

View File

@@ -0,0 +1,45 @@
package collections
interface ImperialMutableList<T> : Iterable<T> {
// Provides access to the number of elements in the list
val size: Int
// Throws an IndexOutOfBoundsException if the given index is not in the range [0, size - 1]. Otherwise, returns the
// element at this index.
operator fun get(index: Int): T
// Throws an IndexOutOfBoundsException if the given index is not in the range [0, size]. Otherwise, inserts the
// given element at this index, shifting any existing elements at or beyond this index so that they appear one
// place later in the list. If index == size then the element is inserted at the end of the list.
fun add(index: Int, element: T)
// Removes all elements from the list.
fun clear()
// Returns true if and only if the given element is contained in the list.
fun contains(element: T): Boolean
// Throws an IndexOutOfBoundsException if the given index is not in the range [0, size - 1]. Otherwise, returns the
// element at this index of the list, while at the same time removing the element from the list. Existing elements
// at larger indices of the list are shifted so that they appear one place earlier in the list.
fun removeAt(index: Int): T
// Searches the list for the first occurrence of the given element. If an occurrence is found, true is returned,
// this occurrence of the element is removed from the list and elements at larger list indices are shifted so that
// they appear one place earlier in the list. False is returned if no occurrence is found.
fun remove(element: T): Boolean
// Throws an IndexOutOfBoundsException if the given index is not in the range [0, size - 1]. Otherwise, sets the
// element at this index to the given element and returns the element that was previously at this index.
operator fun set(index: Int, element: T): T
// Throws an IndexOutOfBoundsException if the given index is not in the range [0, size]. Otherwise, adds the
// elements from "other" to this list, in order, starting at the given index. Any existing elements at or beyond
// this index are moved so that they appear after the newly-inserted elements. If index == size then the elements
// of "other" are inserted at the end of the receiving list.
fun addAll(index: Int, other: ImperialMutableList<T>)
// Returns an iterator that provides access to the elements of the list, in order.
override operator fun iterator(): Iterator<T>
}

View File

@@ -0,0 +1,5 @@
package collections
fun<T,S: T> ImperialMutableList<T>.removeAll(toRemove: ImperialMutableList<S>) {
toRemove.forEach { remove(it) }
}

View File

@@ -0,0 +1,31 @@
package collections
interface ImperialMutableMap<K, V> : Iterable<ImperialMutableMap.Entry<K, V>> {
data class Entry<K, V>(val key: K, var value: V)
// Yields the number of entries in the map.
val size: Int
// Provides an iterator for traversing the entries of the map. This method is
// required by Iterable.
override operator fun iterator(): Iterator<Entry<K, V>>
// Returns the value at 'key', or null if there is no such value.
// This operator allows array-like indexing.
operator fun get(key: K): V?
// Operator version of 'put' to allow array-like indexing.
operator fun set(key: K, value: V): V? = put(key, value)
// Associates 'value' with 'key'. Returns the previous value associated with
// 'key', or null if there is no such previous value.
fun put(key: K, value: V): V?
// Removes entry with key 'key' from the map if such an entry exists, returning
// the associated value if so. Otherwise, returns null.
fun remove(key: K): V?
// Returns true if and only if there is some value associated with 'key' in the map.
fun contains(key: K): Boolean = get(key) != null
}

View File

@@ -0,0 +1,96 @@
package collections
class ResizingArrayList<T>(
private var capacity: Int = 16,
): ImperialMutableList<T> {
override var size = 0
private set
private var elements: Array<T?> = arrayOfNulls(capacity)
init {
if (capacity < 0) throw IllegalArgumentException("Capacity must be positive")
}
override fun get(index: Int): T {
if (index !in 0..<size) throw IndexOutOfBoundsException("Index: $index, Size: $size")
return elements[index]!!
}
@Suppress("UNCHECKED_CAST")
fun resize() {
capacity = maxOf(capacity,1) * 2
var newElements = arrayOfNulls<Any?>(capacity)
elements.forEachIndexed { index,_ ->
newElements[index] = elements[index]
}
elements = newElements as Array<T?>
}
override fun add(index: Int, element: T) {
if (index !in 0..size)
throw IndexOutOfBoundsException("Index: $index, Size: $size")
if (size == capacity) resize()
elements.copyInto(elements,index+1,index,size)
elements[index] = element
size++
}
override fun clear() {
elements.forEachIndexed { index,_ -> elements[index] = null }
size = 0
}
override fun contains(element: T) = elements.contains(element)
override fun removeAt(index: Int): T {
if (index !in 0..<size)
throw IndexOutOfBoundsException("Index: $index, Size: $size")
return elements[index]!!.also {
size--
elements.copyInto(elements,-1,index,size)
}
}
fun find(element: T): Int? {
elements.forEachIndexed { index,value ->
if (value == element) return@find index
}
return null
}
override fun remove(element: T): Boolean {
val index = find(element)
return index?.let {
removeAt(index)
true
} ?: false
}
override fun set(index: Int, element: T): T {
if (index !in 0..<size)
throw IndexOutOfBoundsException("Index: $index, Size: $size")
return elements[index]!!.also {
elements[index] = element
}
}
override fun addAll(index: Int, other: ImperialMutableList<T>) {
while (index + size + other.size !in 0..capacity) resize()
elements.copyInto(elements,index + other.size,index,size)
other.forEachIndexed { otherIndex,value -> elements[index + otherIndex] = value }
size += other.size
//other.toList().forEachIndexed { otherIndex,value -> add(index + otherIndex,value) }
}
inner class ResizingArrayListIterator: Iterator<T> {
private var index = 0
override fun hasNext() = elements.size > index && elements[index] != null
override fun next(): T {
if (!hasNext()) throw NoSuchElementException()
return elements[index++]!!
}
}
override fun iterator() = ResizingArrayListIterator()
override fun toString() = "[${toList().joinToString(", ") { it.toString() }}]"
}

View File

@@ -0,0 +1,163 @@
package collections
class SinglyLinkedList<T> : ImperialMutableList<T> {
private class Node<T>(var element: T, var next: Node<T>? = null)
private var head: Node<T>? = null
override var size: Int = 0
private set
override fun toString(): String {
val result = StringBuilder()
result.append("[")
var current = head
var first = true
while (current != null) {
if (!first) {
result.append(", ")
}
first = false
result.append(current.element)
current = current.next
}
result.append("]")
return result.toString()
}
override fun get(index: Int): T {
checkIndexInBounds(index)
var current: Node<T>? = head
for (i in 0..<index) {
current = current!!.next
}
return current!!.element
}
override fun add(index: Int, element: T) {
checkIndexInBounds(index, true)
size++
val (previous: Node<T>?, current: Node<T>?) = traverseTo(index)
val newNode = Node(element, current)
if (previous == null) {
head = newNode
} else {
previous.next = newNode
}
}
override fun clear() {
head = null
size = 0
}
override fun contains(element: T): Boolean {
var current = head
while (current != null) {
if (current.element == element) {
return true
}
current = current.next
}
return false
}
override fun removeAt(index: Int): T {
checkIndexInBounds(index)
val (previous: Node<T>?, current: Node<T>?) = traverseTo(index)
val result = current!!.element
unlink(previous, current)
return result
}
override fun remove(element: T): Boolean {
var previous: Node<T>? = null
var current: Node<T>? = head
while (current != null) {
if (current.element == element) {
unlink(previous, current)
return true
}
previous = current
current = current.next
}
return false
}
override fun set(index: Int, element: T): T {
checkIndexInBounds(index)
var current: Node<T>? = head
for (i in 0..<index) {
current = current!!.next
}
val result = current!!.element
current.element = element
return result
}
override fun addAll(index: Int, other: ImperialMutableList<T>) {
checkIndexInBounds(index, true)
val iterator = other.iterator()
val start = Node(iterator.next())
var end = start
while (iterator.hasNext()) {
end.next = Node(iterator.next())
end = end.next!!
}
if (index == 0) {
end.next = head
head = start
} else {
val (previous, current) = traverseTo(index)
assert(previous != null)
previous!!.next = start
end.next = current
}
size += other.size
}
override fun iterator(): Iterator<T> = object : Iterator<T> {
private var nextElement: Node<T>? = head
override fun hasNext(): Boolean = nextElement != null
override fun next(): T {
if (!hasNext()) {
throw NoSuchElementException()
}
val result = nextElement!!.element
nextElement = nextElement!!.next
return result
}
}
private fun checkIndexInBounds(
index: Int,
inclusive: Boolean = false,
): Unit =
if (index !in 0..<(if (inclusive) size + 1 else size)) {
throw IndexOutOfBoundsException()
} else {
Unit
}
private fun unlink(previous: Node<T>?, current: Node<T>) {
if (previous == null) {
head = current.next
} else {
previous.next = current.next
}
size--
}
private fun traverseTo(index: Int): Pair<Node<T>?, Node<T>?> {
var previous: Node<T>? = null
var current: Node<T>? = head
for (i in 0..<index) {
previous = current
current = current!!.next
}
return Pair(previous, current)
}
}

View File

@@ -0,0 +1,9 @@
package collections
// Useful imports that you will probably need:
// import java.util.concurrent.atomic.AtomicInteger
// import java.util.concurrent.locks.Lock
// import java.util.concurrent.locks.ReentrantLock
// import kotlin.concurrent.withLock
class StripedHashmap<K, V>

View File

@@ -0,0 +1,19 @@
package collections
import kotlin.test.Test
import kotlin.test.assertFalse
class HashmapTests : ImperialMutableMapTestsParent() {
override fun emptyCustomMutableMapStringInt(): ImperialMutableMap<String, Int> =
Hashmap(::SinglyLinkedList)
override fun emptyCustomMutableMapCollidingStringInt(): ImperialMutableMap<CollidingString, Int> =
Hashmap(::SinglyLinkedList)
// This test is present merely to ensure that at least one concrete test case exists.
// The real tests are inherited from the abstract superclass.
@Test
fun `trivial test`() {
assertFalse(emptyCustomMutableMapStringInt().iterator().hasNext())
}
}

View File

@@ -0,0 +1,46 @@
package collections
import kotlin.test.Test
import kotlin.test.assertEquals
class ImperialMutableListUtilitiesTests {
@Test
fun `removeAll matching list types`() {
val list1: ImperialMutableList<String> = SinglyLinkedList()
list1.add(0, "a")
list1.add(0, "b")
list1.add(0, "c")
list1.add(0, "d")
list1.add(0, "e")
val list2: ImperialMutableList<String> = SinglyLinkedList()
list2.add(0, "a")
list2.add(0, "b")
list2.add(0, "c")
list1.removeAll(list2)
assertEquals(2, list1.size)
assertEquals("e", list1[0])
assertEquals("d", list1[1])
}
@Test
fun `removeAll different list types`() {
val list1: ImperialMutableList<Any> = SinglyLinkedList()
list1.add(0, "a")
list1.add(0, "b")
list1.add(0, "c")
list1.add(0, 1)
list1.add(0, 2)
list1.add(0, 3)
val list2: ImperialMutableList<String> = SinglyLinkedList()
list2.add(0, "a")
list2.add(0, "b")
list2.add(0, "c")
list1.removeAll(list2)
assertEquals(3, list1.size)
assertEquals(3, list1[0])
assertEquals(2, list1[1])
assertEquals(1, list1[2])
}
}

View File

@@ -0,0 +1,257 @@
package collections
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.fail
abstract class ImperialMutableMapTestsParent {
abstract fun emptyCustomMutableMapStringInt(): ImperialMutableMap<String, Int>
abstract fun emptyCustomMutableMapCollidingStringInt(): ImperialMutableMap<CollidingString, Int>
@Test
fun `test contains when empty`() {
val map = emptyCustomMutableMapStringInt()
assertFalse(map.contains("Hello"))
}
@Test
fun `test remove when empty`() {
val map = emptyCustomMutableMapStringInt()
assertNull(map.remove("Hello"))
}
@Test
fun `test size when empty`() {
val map = emptyCustomMutableMapStringInt()
assertEquals(0, map.size)
}
@Test
fun `test contains after put`() {
val map = emptyCustomMutableMapStringInt()
map.put("Hello", 3)
assertTrue(map.contains("Hello"))
}
@Test
fun `test remove after put`() {
val map = emptyCustomMutableMapStringInt()
map.put("Hello", 3)
assertNull(map.remove("World"))
assertEquals(3, map.remove("Hello"))
}
@Test
fun `test size after put`() {
val map = emptyCustomMutableMapStringInt()
map.put("Hello", 3)
assertEquals(1, map.size)
}
@Test
fun `test get returns null`() {
val map = emptyCustomMutableMapStringInt()
map.put("Hello", 3)
map.put("World", 4)
assertNull(map["You"])
}
@Test
fun `test get returns latest value`() {
val map = emptyCustomMutableMapStringInt()
map["Hello"] = 3
map["World"] = 4
map["Hello"] = 10
map["Hello"] = 11
assertEquals(11, map["Hello"])
}
@Test
fun `test entries initially empty`() {
val map = emptyCustomMutableMapStringInt()
for (e in map) {
fail("Map entries should be empty")
}
}
@Test
fun `test entries after some putting`() {
val map = emptyCustomMutableMapStringInt()
val entries = (1..100).map {
ImperialMutableMap.Entry(it.toString(), it)
}
entries.forEach {
map.put(it.key, it.value)
}
assertEquals(entries.size, map.size)
assertEquals(entries, map.toSet().sortedBy { it.value })
}
@Test
fun `test entries after some setting`() {
val map = emptyCustomMutableMapStringInt()
val expected: List<ImperialMutableMap.Entry<String, Int>> = (1..100).map {
ImperialMutableMap.Entry(it.toString(), it)
}
expected.forEach {
map[it.key] = it.value
}
assertEquals(expected.size, map.size)
assertEquals(expected, map.toList().sortedBy { it.value })
}
@Test
fun `test entries after some putting, removing and setting`() {
val map = createCustomMutableMapByPuttingRemovingAndSetting()
val expected = createExpectedEntriesFromPuttingRemovingAndSetting()
assertEquals(expected.size, map.size)
assertEquals(expected, map.toList().sortedBy { it.value })
}
@Test
fun `test entries after some putting (collision prone)`() {
val map = emptyCustomMutableMapCollidingStringInt()
val expected = (1..100).map {
ImperialMutableMap.Entry(CollidingString(it.toString()), it)
}
expected.forEach {
map.put(it.key, it.value)
}
assertEquals(expected.size, map.size)
assertEquals(expected, map.toList().sortedBy { it.value })
}
@Test
fun `test entries after some setting (collision prone)`() {
val map = emptyCustomMutableMapCollidingStringInt()
val expected = (1..100).map {
ImperialMutableMap.Entry(CollidingString(it.toString()), it)
}
expected.forEach {
map[it.key] = it.value
}
assertEquals(expected.size, map.size)
assertEquals(expected, map.toList().sortedBy { it.value })
}
@Test
fun `test entries after some putting, removing and setting (collision prone)`() {
val map = createCollisionProneMapByPuttingRemovingAndSetting()
val expected = createCollisionProneExpectedEntriesFromPuttingRemovingAndSetting()
assertEquals(expected.size, map.size)
assertEquals(expected, map.toList().sortedBy { it.value })
}
@Test
fun `performance test 1`() {
println("Performance test started.")
val map = emptyCustomMutableMapStringInt()
for (i in 0..<1000000) {
if (i.mod(10000) == 0) {
println("Added $i elements out of 1000000. These messages should fly by if performance is adequate.")
}
map.put(i.toString(), i)
}
assertEquals(1000000, map.size)
}
@Test
fun `performance test 2`() {
println("Performance test started.")
val map = emptyCustomMutableMapCollidingStringInt()
for (i in 0..<20000) {
if (i.mod(100) == 0) {
println("Added $i elements out of 20000. These messages should fly by if performance is adequate.")
}
map.put(CollidingString(i.toString()), i)
}
assertEquals(20000, map.size)
}
class CollidingString(val string: String) : Comparable<CollidingString> {
override fun hashCode(): Int = 5
override fun compareTo(other: CollidingString): Int = string.compareTo(other.string)
override fun equals(other: Any?): Boolean {
if (other is CollidingString) {
return string == other.string
}
return false
}
}
private fun createCustomMutableMapByPuttingRemovingAndSetting(): ImperialMutableMap<String, Int> {
val map = emptyCustomMutableMapStringInt()
for (i in 1..100) {
assertFalse(map.contains(i.toString()))
assertNull(map[i.toString()])
assertNull(map.put(i.toString(), i))
}
for (i in 1..100) {
assertTrue(map.contains(i.toString()))
assertEquals(i, map[i.toString()])
if (i % 2 == 0) {
val previous = map.remove(i.toString())
assertNotNull(previous)
assertEquals(i, previous)
}
}
for (i in 1..100) {
if (i % 4 == 0) {
assertNull(map[i.toString()])
assertFalse(map.contains(i.toString()))
assertNull(map.set(i.toString(), i))
}
}
return map
}
private fun createExpectedEntriesFromPuttingRemovingAndSetting(): List<ImperialMutableMap.Entry<String, Int>> {
val entries = (1..100).map {
ImperialMutableMap.Entry(it.toString(), it)
}.filter {
it.value % 2 != 0 || it.value % 4 == 0
}
return entries
}
private fun createCollisionProneMapByPuttingRemovingAndSetting(): ImperialMutableMap<CollidingString, Int> {
val map = emptyCustomMutableMapCollidingStringInt()
for (i in 1..100) {
assertFalse(map.contains(CollidingString(i.toString())))
assertNull(map[CollidingString(i.toString())])
assertNull(map.put(CollidingString(i.toString()), i))
}
for (i in 1..100) {
assertTrue(map.contains(CollidingString(i.toString())))
assertEquals(i, map[CollidingString(i.toString())])
if (i % 2 == 0) {
val previous = map.remove(CollidingString(i.toString()))
assertNotNull(previous)
assertEquals(i, previous)
}
}
for (i in 1..100) {
if (i % 4 == 0) {
assertNull(map.get(CollidingString(i.toString())))
assertFalse(map.contains(CollidingString(i.toString())))
assertNull(map.set(CollidingString(i.toString()), i))
}
}
return map
}
private fun createCollisionProneExpectedEntriesFromPuttingRemovingAndSetting(): List<ImperialMutableMap.Entry<CollidingString, Int>> {
val entries = (1..100).map {
ImperialMutableMap.Entry(CollidingString(it.toString()), it)
}.filter {
it.value % 2 != 0 || it.value % 4 == 0
}
return entries
}
}

View File

@@ -0,0 +1,423 @@
package collections
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
private val exampleStrings: Array<String> = arrayOf(
"cat",
"dog",
"frog",
"horse",
"zebra",
"wildebeest",
"vulture",
"hyena",
"warthog",
"hyrax",
)
class ResizingArrayListTests {
@Test
fun `test get (String)`() {
val list = ResizingArrayList<String>(10)
for (i in 0..<10) {
list.add(i, exampleStrings[i])
assertEquals(i + 1, list.size)
}
for (i in 0..<10) {
assertEquals(exampleStrings[i], list[i])
}
}
@Test
fun `test add (String)`() {
val list = ResizingArrayList<String>(10)
for (i in 1..10) {
list.add(i - 1, exampleStrings[i - 1])
assertEquals(i, list.size)
}
assertEquals(
exampleStrings.joinToString(prefix = "[", postfix = "]"),
list.toString(),
)
}
@Test
fun `test add (String) with zero initial capacity`() {
val list = ResizingArrayList<String>(0)
for (i in 1..10) {
list.add(i - 1, exampleStrings[i - 1])
assertEquals(i, list.size)
}
assertEquals(
exampleStrings.joinToString(prefix = "[", postfix = "]"),
list.toString(),
)
}
@Test
fun `test add with resize (String)`() {
val list = ResizingArrayList<String>(1)
for (i in 0..256) {
list.add(i, i.toString())
}
for (i in 0..256) {
assertEquals(i.toString(), list[i])
}
}
@Test
fun `test add at start with resize (String)`() {
val list = ResizingArrayList<String>(1)
for (i in 0..256) {
list.add(0, i.toString())
}
for (i in 0..256) {
assertEquals(i.toString(), list[256 - i])
}
}
@Test
fun `test add in middle (String)`() {
val list = ResizingArrayList<String>(10)
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
assertEquals(5, list.size)
list.add(3, "blob")
assertEquals(6, list.size)
assertEquals("[dog, frog, horse, blob, zebra, wildebeest]", list.toString())
}
@Test
fun `test add at end (String)`() {
val list = ResizingArrayList<String>(10)
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
list.add(5, "blob")
assertEquals(6, list.size)
assertEquals("[dog, frog, horse, zebra, wildebeest, blob]", list.toString())
}
@Test
fun `test resize in add-at-index if list is full (String)`() {
val list = ResizingArrayList<String>(1)
list.add(0, "blob")
list.add(0, "blib")
assertEquals("blib", list[0])
assertEquals("blob", list[1])
}
@Test
fun `test clear (String)`() {
val list = ResizingArrayList<String>(10)
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
}
@Test
fun `test toString empty (String)`() {
val list = ResizingArrayList<String>()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
val trivialList = ResizingArrayList<String>(0)
assertEquals(0, trivialList.size)
assertEquals("[]", trivialList.toString())
}
@Test
fun `test set (String)`() {
val list = ResizingArrayList<String>()
list.add(0, "one")
list.add(1, "two")
list.add(2, "three")
assertEquals("one", list.set(0, "forty two"))
assertEquals("two", list.set(1, "forty three"))
assertEquals("three", list.set(2, "forty four"))
assertEquals(3, list.size)
assertEquals("[forty two, forty three, forty four]", list.toString())
}
@Test
fun `test get (Int)`() {
val list = ResizingArrayList<Int>(10)
for (i in 1..10) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
for (i in 1..10) {
assertEquals(i, list[i - 1])
}
}
@Test
fun `test add (Int)`() {
val list = ResizingArrayList<Int>(10)
for (i in 1..10) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", list.toString())
}
@Test
fun `test add with resize (Int)`() {
val list = ResizingArrayList<Int>(1)
for (i in 0..256) {
list.add(i, i)
}
for (i in 0..256) {
assertEquals(i, list[i])
}
}
@Test
fun `test add in middle (Int)`() {
val list = ResizingArrayList<Int>(10)
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
assertEquals(5, list.size)
list.add(3, 42)
assertEquals(6, list.size)
assertEquals("[1, 2, 3, 42, 4, 5]", list.toString())
}
@Test
fun `test add at end (Int)`() {
val list = ResizingArrayList<Int>(10)
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
list.add(5, 42)
assertEquals(6, list.size)
assertEquals("[1, 2, 3, 4, 5, 42]", list.toString())
}
@Test
fun `test resize in add-at-index if list is full (Int)`() {
val list = ResizingArrayList<Int>(1)
list.add(0, 1)
list.add(0, 2)
assertEquals(2, list[0])
assertEquals(1, list[1])
}
@Test
fun `test clear (Int)`() {
val list = ResizingArrayList<Int>(10)
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
}
@Test
fun `test toString empty (Int)`() {
val list = ResizingArrayList<Int>(10)
assertEquals(0, list.size)
assertEquals("[]", list.toString())
val trivialList = ResizingArrayList<Int>(0)
assertEquals(0, trivialList.size)
assertEquals("[]", trivialList.toString())
}
@Test
fun `test set (Int)`() {
val list = ResizingArrayList<Int>(10)
list.add(0, 1)
list.add(1, 2)
list.add(2, 3)
assertEquals(1, list.set(0, 42))
assertEquals(2, list.set(1, 43))
assertEquals(3, list.set(2, 44))
assertEquals(3, list.size)
assertEquals("[42, 43, 44]", list.toString())
}
@Test
fun `test addAll to zero-capacity list`() {
val initiallyEmptyList: ImperialMutableList<Int> = ResizingArrayList(0)
val smallList: ImperialMutableList<Int> = ResizingArrayList(2)
for (i in 1..10) {
smallList.add(i - 1, i)
}
initiallyEmptyList.addAll(0, smallList)
assertEquals(10, initiallyEmptyList.size)
assertEquals(1, initiallyEmptyList[0])
assertEquals(2, initiallyEmptyList[1])
assertEquals(3, initiallyEmptyList[2])
assertEquals(4, initiallyEmptyList[3])
assertEquals(5, initiallyEmptyList[4])
assertEquals(6, initiallyEmptyList[5])
assertEquals(7, initiallyEmptyList[6])
assertEquals(8, initiallyEmptyList[7])
assertEquals(9, initiallyEmptyList[8])
assertEquals(10, initiallyEmptyList[9])
}
@Test
fun `test addAll small`() {
val firstSmallList: ImperialMutableList<Int> = ResizingArrayList(2)
val secondSmallList: ImperialMutableList<Int> = ResizingArrayList(2)
for (i in 1..10) {
firstSmallList.add(i - 1, i)
secondSmallList.add(i - 1, -i)
}
firstSmallList.addAll(firstSmallList.size, secondSmallList)
assertEquals(20, firstSmallList.size)
assertEquals(1, firstSmallList[0])
assertEquals(2, firstSmallList[1])
assertEquals(3, firstSmallList[2])
assertEquals(4, firstSmallList[3])
assertEquals(5, firstSmallList[4])
assertEquals(6, firstSmallList[5])
assertEquals(7, firstSmallList[6])
assertEquals(8, firstSmallList[7])
assertEquals(9, firstSmallList[8])
assertEquals(10, firstSmallList[9])
assertEquals(-1, firstSmallList[10])
assertEquals(-2, firstSmallList[11])
assertEquals(-3, firstSmallList[12])
assertEquals(-4, firstSmallList[13])
assertEquals(-5, firstSmallList[14])
assertEquals(-6, firstSmallList[15])
assertEquals(-7, firstSmallList[16])
assertEquals(-8, firstSmallList[17])
assertEquals(-9, firstSmallList[18])
assertEquals(-10, firstSmallList[19])
}
@Test
fun `test addAll at index small`() {
val firstSmallList: ImperialMutableList<Int> = ResizingArrayList(2)
val secondSmallList: ImperialMutableList<Int> = ResizingArrayList(2)
for (i in 1..10) {
firstSmallList.add(i - 1, i)
secondSmallList.add(i - 1, -i)
}
firstSmallList.addAll(5, secondSmallList)
assertEquals(20, firstSmallList.size)
assertEquals(1, firstSmallList[0])
assertEquals(2, firstSmallList[1])
assertEquals(3, firstSmallList[2])
assertEquals(4, firstSmallList[3])
assertEquals(5, firstSmallList[4])
assertEquals(-1, firstSmallList[5])
assertEquals(-2, firstSmallList[6])
assertEquals(-3, firstSmallList[7])
assertEquals(-4, firstSmallList[8])
assertEquals(-5, firstSmallList[9])
assertEquals(-6, firstSmallList[10])
assertEquals(-7, firstSmallList[11])
assertEquals(-8, firstSmallList[12])
assertEquals(-9, firstSmallList[13])
assertEquals(-10, firstSmallList[14])
assertEquals(6, firstSmallList[15])
assertEquals(7, firstSmallList[16])
assertEquals(8, firstSmallList[17])
assertEquals(9, firstSmallList[18])
assertEquals(10, firstSmallList[19])
}
@Test
fun `test addAll at start`() {
val list1: ImperialMutableList<Int> = ResizingArrayList()
val list2: ImperialMutableList<Int> = ResizingArrayList()
list1.add(0, 1)
list1.add(1, 2)
list1.add(2, 3)
list2.add(0, 4)
list2.add(1, 5)
list2.add(2, 6)
list1.addAll(0, list2)
assertEquals(6, list1.size)
assertEquals(4, list1[0])
assertEquals(5, list1[1])
assertEquals(6, list1[2])
assertEquals(1, list1[3])
assertEquals(2, list1[4])
assertEquals(3, list1[5])
}
@Test
fun `test addAll at end`() {
val list1: ImperialMutableList<Int> = ResizingArrayList()
val list2: ImperialMutableList<Int> = ResizingArrayList()
list1.add(0, 1)
list1.add(1, 2)
list1.add(2, 3)
list2.add(0, 4)
list2.add(1, 5)
list2.add(2, 6)
list1.addAll(3, list2)
assertEquals(6, list1.size)
assertEquals(1, list1[0])
assertEquals(2, list1[1])
assertEquals(3, list1[2])
assertEquals(4, list1[3])
assertEquals(5, list1[4])
assertEquals(6, list1[5])
}
@Test
fun `size is 0`() {
assertEquals(0, ResizingArrayList<Int>().size)
}
@Test
fun `iterator has next when empty`() {
assertFalse(ResizingArrayList<Int>().iterator().hasNext())
}
@Test
fun `iterate over elements`() {
val list: ImperialMutableList<String> = ResizingArrayList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
val iterator = list.iterator()
assertTrue(iterator.hasNext())
assertEquals("a", iterator.next())
assertTrue(iterator.hasNext())
assertEquals("b", iterator.next())
assertTrue(iterator.hasNext())
assertEquals("c", iterator.next())
assertFalse(iterator.hasNext())
}
@Test
fun `concatenate with iterator`() {
val list: ImperialMutableList<String> = ResizingArrayList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
val concatenation = StringBuilder()
for (string in list) {
concatenation.append(string)
}
assertEquals("abc", concatenation.toString())
}
}

View File

@@ -0,0 +1,88 @@
package collections
import kotlin.test.Test
import kotlin.test.fail
// These are examples of the kinds of imports that may be useful when writing tests.
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class SinglyLinkedListExtraTests {
/* TODO - replace this placeholder test with a number of your own tests.
@Test
fun `placeholder test`() {
fail()
}
• The tests you write for remove should confirm that the method behaves correctly
when invoked with an element that does appear in the list, and when invoked with
an element that does not appear in the list.
• Each test should be designed with a specific purpose, and the name of the test should
reflect this purpose.
• Think about “edge case” inputs to these functions when deciding on test inputs.
*/
@Test
fun `remove element in list`() {
val list: ImperialMutableList<String> = SinglyLinkedList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
assertEquals(list.remove("b"),true)
assertEquals(list.remove("c"),true)
assertEquals(list.remove("a"),true)
}
@Test
fun `removeAt in list`() {
val list: ImperialMutableList<String> = SinglyLinkedList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
assertEquals(list.removeAt(1),"b")
assertEquals(list.removeAt(1),"c")
assertEquals(list.removeAt(0),"a")
}
@Test
fun `remove element not in list`() {
val list: ImperialMutableList<String> = SinglyLinkedList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
assertEquals(list.remove("d"),false)
assertEquals(list.remove("a"),true)
assertEquals(list.remove("b"),true)
assertEquals(list.remove("c"),true)
assertEquals(list.remove("d"),false)
}
@Test
fun `remove empty list throws`() {
val list: ImperialMutableList<String> = SinglyLinkedList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
assertEquals(list.removeAt(1),"b")
assertEquals(list.removeAt(1),"c")
assertEquals(list.removeAt(0),"a")
try {
list.removeAt(0)
} catch (exception: IndexOutOfBoundsException) {
// yay
}
}
@Test
fun `remove list out of bounds`() {
val list: ImperialMutableList<String> = SinglyLinkedList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
try {
list.removeAt(3)
} catch (exception: IndexOutOfBoundsException) {
// yay
}
}
}

View File

@@ -0,0 +1,335 @@
package collections
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
private val exampleStrings: Array<String> = arrayOf(
"cat",
"dog",
"frog",
"horse",
"zebra",
"wildebeest",
"vulture",
"hyena",
"warthog",
"hyrax",
)
class SinglyLinkedListJavaTests {
@Test
fun `test get (String)`() {
val list = SinglyLinkedListJava<String>()
for (i in 0..<10) {
list.add(i, exampleStrings[i])
assertEquals(i + 1, list.size)
}
for (i in 0..<10) {
assertEquals(exampleStrings[i], list[i])
}
}
@Test
fun `test add (String)`() {
val list = SinglyLinkedListJava<String>()
for (i in 1..10) {
list.add(i - 1, exampleStrings[i - 1])
assertEquals(i, list.size)
}
assertEquals(
exampleStrings.joinToString(prefix = "[", postfix = "]"),
list.toString(),
)
}
@Test
fun `test add in middle (String)`() {
val list = SinglyLinkedListJava<String>()
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
assertEquals(5, list.size)
list.add(3, "blob")
assertEquals(6, list.size)
assertEquals("[dog, frog, horse, blob, zebra, wildebeest]", list.toString())
}
@Test
fun `test add at end (String)`() {
val list = SinglyLinkedListJava<String>()
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
list.add(5, "blob")
assertEquals(6, list.size)
assertEquals("[dog, frog, horse, zebra, wildebeest, blob]", list.toString())
}
@Test
fun `test clear (String)`() {
val list = SinglyLinkedListJava<String>()
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
}
@Test
fun `test toString empty (String)`() {
val list = SinglyLinkedListJava<String>()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
}
@Test
fun `test set (String)`() {
val list = SinglyLinkedListJava<String>()
list.add(0, "one")
list.add(1, "two")
list.add(2, "three")
assertEquals("one", list.set(0, "forty two"))
assertEquals("two", list.set(1, "forty three"))
assertEquals("three", list.set(2, "forty four"))
assertEquals(3, list.size)
assertEquals("[forty two, forty three, forty four]", list.toString())
}
@Test
fun `test get (Int)`() {
val list = SinglyLinkedListJava<Int>()
for (i in 1..10) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
for (i in 1..10) {
assertEquals(i, list[i - 1])
}
}
@Test
fun `test add (Int)`() {
val list = SinglyLinkedListJava<Int>()
for (i in 1..10) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", list.toString())
}
@Test
fun `test add in middle (Int)`() {
val list = SinglyLinkedListJava<Int>()
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
assertEquals(5, list.size)
list.add(3, 42)
assertEquals(6, list.size)
assertEquals("[1, 2, 3, 42, 4, 5]", list.toString())
}
@Test
fun `test add at end (Int)`() {
val list = SinglyLinkedListJava<Int>()
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
list.add(5, 42)
assertEquals(6, list.size)
assertEquals("[1, 2, 3, 4, 5, 42]", list.toString())
}
@Test
fun `test clear (Int)`() {
val list = SinglyLinkedListJava<Int>()
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
}
@Test
fun `test toString empty (Int)`() {
val list = SinglyLinkedListJava<Int>()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
val trivialList = SinglyLinkedListJava<Int>()
assertEquals(0, trivialList.size)
assertEquals("[]", trivialList.toString())
}
@Test
fun `test set (Int)`() {
val list = SinglyLinkedListJava<Int>()
list.add(0, 1)
list.add(1, 2)
list.add(2, 3)
assertEquals(1, list.set(0, 42))
assertEquals(2, list.set(1, 43))
assertEquals(3, list.set(2, 44))
assertEquals(3, list.size)
assertEquals("[42, 43, 44]", list.toString())
}
@Test
fun `test addAll small`() {
val firstSmallList: ImperialMutableList<Int> = SinglyLinkedListJava()
val secondSmallList: ImperialMutableList<Int> = SinglyLinkedListJava()
for (i in 1..10) {
firstSmallList.add(i - 1, i)
secondSmallList.add(i - 1, -i)
}
firstSmallList.addAll(firstSmallList.size, secondSmallList)
assertEquals(20, firstSmallList.size)
assertEquals(1, firstSmallList[0])
assertEquals(2, firstSmallList[1])
assertEquals(3, firstSmallList[2])
assertEquals(4, firstSmallList[3])
assertEquals(5, firstSmallList[4])
assertEquals(6, firstSmallList[5])
assertEquals(7, firstSmallList[6])
assertEquals(8, firstSmallList[7])
assertEquals(9, firstSmallList[8])
assertEquals(10, firstSmallList[9])
assertEquals(-1, firstSmallList[10])
assertEquals(-2, firstSmallList[11])
assertEquals(-3, firstSmallList[12])
assertEquals(-4, firstSmallList[13])
assertEquals(-5, firstSmallList[14])
assertEquals(-6, firstSmallList[15])
assertEquals(-7, firstSmallList[16])
assertEquals(-8, firstSmallList[17])
assertEquals(-9, firstSmallList[18])
assertEquals(-10, firstSmallList[19])
}
@Test
fun `test addAll at index small`() {
val firstSmallList: ImperialMutableList<Int> = SinglyLinkedListJava()
val secondSmallList: ImperialMutableList<Int> = SinglyLinkedListJava()
for (i in 1..10) {
firstSmallList.add(i - 1, i)
secondSmallList.add(i - 1, -i)
}
firstSmallList.addAll(5, secondSmallList)
assertEquals(20, firstSmallList.size)
assertEquals(1, firstSmallList[0])
assertEquals(2, firstSmallList[1])
assertEquals(3, firstSmallList[2])
assertEquals(4, firstSmallList[3])
assertEquals(5, firstSmallList[4])
assertEquals(-1, firstSmallList[5])
assertEquals(-2, firstSmallList[6])
assertEquals(-3, firstSmallList[7])
assertEquals(-4, firstSmallList[8])
assertEquals(-5, firstSmallList[9])
assertEquals(-6, firstSmallList[10])
assertEquals(-7, firstSmallList[11])
assertEquals(-8, firstSmallList[12])
assertEquals(-9, firstSmallList[13])
assertEquals(-10, firstSmallList[14])
assertEquals(6, firstSmallList[15])
assertEquals(7, firstSmallList[16])
assertEquals(8, firstSmallList[17])
assertEquals(9, firstSmallList[18])
assertEquals(10, firstSmallList[19])
}
@Test
fun `test addAll at start`() {
val list1: ImperialMutableList<Int> = SinglyLinkedListJava()
val list2: ImperialMutableList<Int> = SinglyLinkedListJava()
list1.add(0, 1)
list1.add(1, 2)
list1.add(2, 3)
list2.add(0, 4)
list2.add(1, 5)
list2.add(2, 6)
list1.addAll(0, list2)
assertEquals(6, list1.size)
assertEquals(4, list1[0])
assertEquals(5, list1[1])
assertEquals(6, list1[2])
assertEquals(1, list1[3])
assertEquals(2, list1[4])
assertEquals(3, list1[5])
}
@Test
fun `test addAll at end`() {
val list1: ImperialMutableList<Int> = SinglyLinkedListJava()
val list2: ImperialMutableList<Int> = SinglyLinkedListJava()
list1.add(0, 1)
list1.add(1, 2)
list1.add(2, 3)
list2.add(0, 4)
list2.add(1, 5)
list2.add(2, 6)
list1.addAll(3, list2)
assertEquals(6, list1.size)
assertEquals(1, list1[0])
assertEquals(2, list1[1])
assertEquals(3, list1[2])
assertEquals(4, list1[3])
assertEquals(5, list1[4])
assertEquals(6, list1[5])
}
@Test
fun `size is 0`() {
assertEquals(0, SinglyLinkedListJava<Int>().size)
}
@Test
fun `iterator has next when empty`() {
assertFalse(SinglyLinkedListJava<Int>().iterator().hasNext())
}
@Test
fun `iterate over elements`() {
val list: ImperialMutableList<String> = SinglyLinkedListJava()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
val iterator = list.iterator()
assertTrue(iterator.hasNext())
assertEquals("a", iterator.next())
assertTrue(iterator.hasNext())
assertEquals("b", iterator.next())
assertTrue(iterator.hasNext())
assertEquals("c", iterator.next())
assertFalse(iterator.hasNext())
}
@Test
fun `concatenate with iterator`() {
val list: ImperialMutableList<String> = SinglyLinkedListJava()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
val concatenation = StringBuilder()
for (string in list) {
concatenation.append(string)
}
assertEquals("abc", concatenation.toString())
}
}

View File

@@ -0,0 +1,335 @@
package collections
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
private val exampleStrings: Array<String> = arrayOf(
"cat",
"dog",
"frog",
"horse",
"zebra",
"wildebeest",
"vulture",
"hyena",
"warthog",
"hyrax",
)
class SinglyLinkedListTests {
@Test
fun `test get (String)`() {
val list = SinglyLinkedList<String>()
for (i in 0..<10) {
list.add(i, exampleStrings[i])
assertEquals(i + 1, list.size)
}
for (i in 0..<10) {
assertEquals(exampleStrings[i], list[i])
}
}
@Test
fun `test add (String)`() {
val list = SinglyLinkedList<String>()
for (i in 1..10) {
list.add(i - 1, exampleStrings[i - 1])
assertEquals(i, list.size)
}
assertEquals(
exampleStrings.joinToString(prefix = "[", postfix = "]"),
list.toString(),
)
}
@Test
fun `test add in middle (String)`() {
val list = SinglyLinkedList<String>()
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
assertEquals(5, list.size)
list.add(3, "blob")
assertEquals(6, list.size)
assertEquals("[dog, frog, horse, blob, zebra, wildebeest]", list.toString())
}
@Test
fun `test add at end (String)`() {
val list = SinglyLinkedList<String>()
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
list.add(5, "blob")
assertEquals(6, list.size)
assertEquals("[dog, frog, horse, zebra, wildebeest, blob]", list.toString())
}
@Test
fun `test clear (String)`() {
val list = SinglyLinkedList<String>()
for (i in 1..5) {
list.add(i - 1, exampleStrings[i])
assertEquals(i, list.size)
}
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
}
@Test
fun `test toString empty (String)`() {
val list = SinglyLinkedList<String>()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
}
@Test
fun `test set (String)`() {
val list = SinglyLinkedList<String>()
list.add(0, "one")
list.add(1, "two")
list.add(2, "three")
assertEquals("one", list.set(0, "forty two"))
assertEquals("two", list.set(1, "forty three"))
assertEquals("three", list.set(2, "forty four"))
assertEquals(3, list.size)
assertEquals("[forty two, forty three, forty four]", list.toString())
}
@Test
fun `test get (Int)`() {
val list = SinglyLinkedList<Int>()
for (i in 1..10) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
for (i in 1..10) {
assertEquals(i, list[i - 1])
}
}
@Test
fun `test add (Int)`() {
val list = SinglyLinkedList<Int>()
for (i in 1..10) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", list.toString())
}
@Test
fun `test add in middle (Int)`() {
val list = SinglyLinkedList<Int>()
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
assertEquals(5, list.size)
list.add(3, 42)
assertEquals(6, list.size)
assertEquals("[1, 2, 3, 42, 4, 5]", list.toString())
}
@Test
fun `test add at end (Int)`() {
val list = SinglyLinkedList<Int>()
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
list.add(5, 42)
assertEquals(6, list.size)
assertEquals("[1, 2, 3, 4, 5, 42]", list.toString())
}
@Test
fun `test clear (Int)`() {
val list = SinglyLinkedList<Int>()
for (i in 1..5) {
list.add(i - 1, i)
assertEquals(i, list.size)
}
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
list.clear()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
}
@Test
fun `test toString empty (Int)`() {
val list = SinglyLinkedList<Int>()
assertEquals(0, list.size)
assertEquals("[]", list.toString())
val trivialList = SinglyLinkedList<Int>()
assertEquals(0, trivialList.size)
assertEquals("[]", trivialList.toString())
}
@Test
fun `test set (Int)`() {
val list = SinglyLinkedList<Int>()
list.add(0, 1)
list.add(1, 2)
list.add(2, 3)
assertEquals(1, list.set(0, 42))
assertEquals(2, list.set(1, 43))
assertEquals(3, list.set(2, 44))
assertEquals(3, list.size)
assertEquals("[42, 43, 44]", list.toString())
}
@Test
fun `test addAll small`() {
val firstSmallList: ImperialMutableList<Int> = SinglyLinkedList()
val secondSmallList: ImperialMutableList<Int> = SinglyLinkedList()
for (i in 1..10) {
firstSmallList.add(i - 1, i)
secondSmallList.add(i - 1, -i)
}
firstSmallList.addAll(firstSmallList.size, secondSmallList)
assertEquals(20, firstSmallList.size)
assertEquals(1, firstSmallList[0])
assertEquals(2, firstSmallList[1])
assertEquals(3, firstSmallList[2])
assertEquals(4, firstSmallList[3])
assertEquals(5, firstSmallList[4])
assertEquals(6, firstSmallList[5])
assertEquals(7, firstSmallList[6])
assertEquals(8, firstSmallList[7])
assertEquals(9, firstSmallList[8])
assertEquals(10, firstSmallList[9])
assertEquals(-1, firstSmallList[10])
assertEquals(-2, firstSmallList[11])
assertEquals(-3, firstSmallList[12])
assertEquals(-4, firstSmallList[13])
assertEquals(-5, firstSmallList[14])
assertEquals(-6, firstSmallList[15])
assertEquals(-7, firstSmallList[16])
assertEquals(-8, firstSmallList[17])
assertEquals(-9, firstSmallList[18])
assertEquals(-10, firstSmallList[19])
}
@Test
fun `test addAll at index small`() {
val firstSmallList: ImperialMutableList<Int> = SinglyLinkedList()
val secondSmallList: ImperialMutableList<Int> = SinglyLinkedList()
for (i in 1..10) {
firstSmallList.add(i - 1, i)
secondSmallList.add(i - 1, -i)
}
firstSmallList.addAll(5, secondSmallList)
assertEquals(20, firstSmallList.size)
assertEquals(1, firstSmallList[0])
assertEquals(2, firstSmallList[1])
assertEquals(3, firstSmallList[2])
assertEquals(4, firstSmallList[3])
assertEquals(5, firstSmallList[4])
assertEquals(-1, firstSmallList[5])
assertEquals(-2, firstSmallList[6])
assertEquals(-3, firstSmallList[7])
assertEquals(-4, firstSmallList[8])
assertEquals(-5, firstSmallList[9])
assertEquals(-6, firstSmallList[10])
assertEquals(-7, firstSmallList[11])
assertEquals(-8, firstSmallList[12])
assertEquals(-9, firstSmallList[13])
assertEquals(-10, firstSmallList[14])
assertEquals(6, firstSmallList[15])
assertEquals(7, firstSmallList[16])
assertEquals(8, firstSmallList[17])
assertEquals(9, firstSmallList[18])
assertEquals(10, firstSmallList[19])
}
@Test
fun `test addAll at start`() {
val list1: ImperialMutableList<Int> = SinglyLinkedList()
val list2: ImperialMutableList<Int> = SinglyLinkedList()
list1.add(0, 1)
list1.add(1, 2)
list1.add(2, 3)
list2.add(0, 4)
list2.add(1, 5)
list2.add(2, 6)
list1.addAll(0, list2)
assertEquals(6, list1.size)
assertEquals(4, list1[0])
assertEquals(5, list1[1])
assertEquals(6, list1[2])
assertEquals(1, list1[3])
assertEquals(2, list1[4])
assertEquals(3, list1[5])
}
@Test
fun `test addAll at end`() {
val list1: ImperialMutableList<Int> = SinglyLinkedList()
val list2: ImperialMutableList<Int> = SinglyLinkedList()
list1.add(0, 1)
list1.add(1, 2)
list1.add(2, 3)
list2.add(0, 4)
list2.add(1, 5)
list2.add(2, 6)
list1.addAll(3, list2)
assertEquals(6, list1.size)
assertEquals(1, list1[0])
assertEquals(2, list1[1])
assertEquals(3, list1[2])
assertEquals(4, list1[3])
assertEquals(5, list1[4])
assertEquals(6, list1[5])
}
@Test
fun `size is 0`() {
assertEquals(0, SinglyLinkedList<Int>().size)
}
@Test
fun `iterator has next when empty`() {
assertFalse(SinglyLinkedList<Int>().iterator().hasNext())
}
@Test
fun `iterate over elements`() {
val list: ImperialMutableList<String> = SinglyLinkedList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
val iterator = list.iterator()
assertTrue(iterator.hasNext())
assertEquals("a", iterator.next())
assertTrue(iterator.hasNext())
assertEquals("b", iterator.next())
assertTrue(iterator.hasNext())
assertEquals("c", iterator.next())
assertFalse(iterator.hasNext())
}
@Test
fun `concatenate with iterator`() {
val list: ImperialMutableList<String> = SinglyLinkedList()
list.add(0, "a")
list.add(1, "b")
list.add(2, "c")
val concatenation = StringBuilder()
for (string in list) {
concatenation.append(string)
}
assertEquals("abc", concatenation.toString())
}
}

View File

@@ -0,0 +1,21 @@
package collections
/*
import kotlin.test.Test
import kotlin.test.assertFalse
class StripedHashmapTests : ThreadSafeImperialMutableMapTestsParent() {
override fun emptyThreadSafeMapIntString(): ImperialMutableMap<Int, String> = StripedHashmap(::SinglyLinkedList)
override fun emptyCustomMutableMapStringInt(): ImperialMutableMap<String, Int> = StripedHashmap(::SinglyLinkedList)
override fun emptyCustomMutableMapCollidingStringInt(): ImperialMutableMap<CollidingString, Int> = StripedHashmap(::SinglyLinkedList)
// This test is present merely to ensure that at least one concrete test case exists.
// The real tests are inherited from the abstract superclass.
@Test
fun `trivial test`() {
assertFalse(emptyCustomMutableMapStringInt().iterator().hasNext())
}
}
*/

View File

@@ -0,0 +1,238 @@
package collections
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.test.fail
private class ExceptionMonitoringThread(
private val exceptions: MutableList<Exception>,
private val lock: Lock,
private var body: () -> Unit,
) : Runnable {
override fun run() {
try {
body()
} catch (exception: Exception) {
lock.withLock() {
exceptions.add(exception)
}
}
}
}
abstract class ThreadSafeImperialMutableMapTestsParent : ImperialMutableMapTestsParent() {
abstract fun emptyThreadSafeMapIntString(): ImperialMutableMap<Int, String>
@Suppress("SameParameterValue")
private fun addElementsInRandomOrder(
map: ImperialMutableMap<Int, String>,
lowerBound: Int,
numElements: Int,
seed: Int,
) {
val randomGenerator = Random(seed)
val remaining = mutableListOf<Int>()
(lowerBound..<lowerBound + numElements).forEach {
remaining.add(it)
}
while (remaining.isNotEmpty()) {
val index = randomGenerator.nextInt(remaining.size)
val toAdd = remaining.removeAt(index)
map.put(toAdd, toAdd.toString())
}
}
@Suppress("SameParameterValue")
private fun removeElementsInRandomOrder(
map: ImperialMutableMap<Int, String>,
lowerBound: Int,
numElements: Int,
seed: Int,
) {
val randomGenerator = Random(seed)
val remaining = mutableListOf<Int>()
(lowerBound..<lowerBound + numElements).forEach {
remaining.add(it)
}
while (remaining.isNotEmpty()) {
val index = randomGenerator.nextInt(remaining.size)
val toRemove = remaining.removeAt(index)
map.remove(toRemove)
}
}
private fun runConcurrencyTest(
repeatRuns: Int,
threadBodies: List<(ImperialMutableMap<Int, String>) -> Unit>,
initialEntries: List<ImperialMutableMap.Entry<Int, String>>,
expectedInFinalResult: Set<ImperialMutableMap.Entry<Int, String>>,
notExpectedInFinalResult: Set<ImperialMutableMap.Entry<Int, String>>,
expectedFinalSize: Int? = null,
) {
for (i in 1..repeatRuns) {
println("Repeat run $i of $repeatRuns")
val theMap = emptyThreadSafeMapIntString()
for (entry in initialEntries) {
theMap.put(entry.key, entry.value)
}
val exceptions = mutableListOf<Exception>()
val lock = ReentrantLock()
val threads = threadBodies.map {
Thread(
ExceptionMonitoringThread(
exceptions,
lock,
) { it(theMap) },
)
}
threads.forEach(Thread::start)
threads.forEach(Thread::join)
if (exceptions.isNotEmpty()) {
System.err.println("Exceptions thrown by thread(s):")
for (exception in exceptions) {
System.err.println(exception)
}
fail()
}
val finalContentsAsList = theMap.toList()
val finalContentsAsSet = finalContentsAsList.toSet()
// There should be no difference in the size of final contents as a list vs. as a set
assertEquals(finalContentsAsList.size, finalContentsAsSet.size)
assertTrue(finalContentsAsSet.containsAll(expectedInFinalResult))
assertTrue((finalContentsAsSet intersect notExpectedInFinalResult).isEmpty())
expectedFinalSize?.let {
assertEquals(it, finalContentsAsList.size)
}
}
}
@Test
fun `one thread adds, one thread removes`() {
val chunkSize = 1 shl 12
val adderBody: (ImperialMutableMap<Int, String>) -> Unit = { theMap ->
Thread.sleep(1)
addElementsInRandomOrder(
map = theMap,
lowerBound = 0,
numElements = 2 * chunkSize,
seed = 0,
)
}
val removerBody: (ImperialMutableMap<Int, String>) -> Unit = { theMap ->
Thread.sleep(1)
removeElementsInRandomOrder(
map = theMap,
lowerBound = chunkSize,
numElements = 3 * chunkSize,
seed = 0,
)
}
runConcurrencyTest(
repeatRuns = 10,
threadBodies = listOf(adderBody, removerBody),
initialEntries = (chunkSize..<3 * chunkSize).map { ImperialMutableMap.Entry(it, it.toString()) },
expectedInFinalResult = (0..<chunkSize).map { ImperialMutableMap.Entry(it, it.toString()) }.toSet(),
notExpectedInFinalResult = (chunkSize * 2..<3 * chunkSize).map { ImperialMutableMap.Entry(it, it.toString()) }.toSet(),
)
}
@Test
fun `eight threads add, eight threads remove`() {
val chunkSize = 1 shl 12
val adderBodies: List<(ImperialMutableMap<Int, String>) -> Unit> = (0..<8).map { seed ->
{ theMap ->
addElementsInRandomOrder(
map = theMap,
lowerBound = 0,
numElements = 2 * chunkSize,
seed = seed,
)
}
}
val removerBodies: List<(ImperialMutableMap<Int, String>) -> Unit> = (8..<16).map { seed ->
{ theMap ->
removeElementsInRandomOrder(
map = theMap,
lowerBound = chunkSize,
numElements = 3 * chunkSize,
seed = seed,
)
}
}
runConcurrencyTest(
repeatRuns = 8,
threadBodies = adderBodies + removerBodies,
initialEntries = (chunkSize..<3 * chunkSize).map { ImperialMutableMap.Entry(it, it.toString()) },
expectedInFinalResult = (0..<chunkSize).map { ImperialMutableMap.Entry(it, it.toString()) }.toSet(),
notExpectedInFinalResult = (chunkSize * 2..<3 * chunkSize).map { ImperialMutableMap.Entry(it, it.toString()) }.toSet(),
)
}
@Test
fun `size changes must not be observed during resizing`() {
val worker: (ImperialMutableMap<Int, String>) -> Unit = {
(10..1000).forEach { key ->
it[key] = key.toString()
}
}
val monitor: (ImperialMutableMap<Int, String>) -> Unit = {
while (it.size <= 1000) {
if (it.size < 10) {
it.put(-1, "-1")
}
}
}
runConcurrencyTest(
repeatRuns = 10,
threadBodies = listOf(worker, monitor),
initialEntries = (0..<10).map {
ImperialMutableMap.Entry(it, it.toString())
},
expectedInFinalResult = (0..1000).map {
ImperialMutableMap.Entry(it, it.toString())
}.toSet(),
notExpectedInFinalResult = setOf(ImperialMutableMap.Entry(-1, "-1")),
)
}
@Test
fun `eight threads add`() {
val adderBodies: List<(ImperialMutableMap<Int, String>) -> Unit> = (0..<8).map { threadId ->
{ theMap ->
for (i in 0..<10000) {
val number = threadId * 10000 + i
theMap.put(number, number.toString())
}
}
}
runConcurrencyTest(
repeatRuns = 8,
threadBodies = adderBodies,
initialEntries = emptyList(),
expectedInFinalResult = (0..<80000).map { ImperialMutableMap.Entry(it, it.toString()) }.toSet(),
notExpectedInFinalResult = emptySet(),
expectedFinalSize = 80000,
)
}
@Test
fun `no deadlock in concurrent resize`() {
runConcurrencyTest(
repeatRuns = 100,
threadBodies = (0..<32).map {
{ theMap ->
theMap.put(it, it.toString())
}
},
initialEntries = emptyList(),
expectedInFinalResult = emptySet(),
notExpectedInFinalResult = emptySet(),
)
}
}