Initial.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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--;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package collections
|
||||
|
||||
typealias Bucket<K, V> = ImperialMutableList<ImperialMutableMap.Entry<K, V>>
|
||||
|
||||
const val MAX_LOAD_FACTOR = 0.75
|
||||
@@ -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>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package collections
|
||||
|
||||
fun<T,S: T> ImperialMutableList<T>.removeAll(toRemove: ImperialMutableList<S>) {
|
||||
toRemove.forEach { remove(it) }
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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() }}]"
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user