-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Immutable Set/List/Map/Iterator/ListIterator
- Loading branch information
Showing
7 changed files
with
424 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
public final class io/matthewnelson/immutable/collections/Immutable { | ||
public static final fun listOf (Ljava/util/Collection;)Ljava/util/List; | ||
public static final fun listOf ([Ljava/lang/Object;)Ljava/util/List; | ||
public static final fun mapOf (Ljava/util/Map;)Ljava/util/Map; | ||
public static final fun mapOf ([Lkotlin/Pair;)Ljava/util/Map; | ||
public static final fun setOf (Ljava/util/Collection;)Ljava/util/Set; | ||
public static final fun setOf ([Ljava/lang/Object;)Ljava/util/Set; | ||
} | ||
|
18 changes: 0 additions & 18 deletions
18
...y/collections/src/commonMain/kotlin/io/matthewnelson/immutable/collections/Collections.kt
This file was deleted.
Oops, something went wrong.
142 changes: 142 additions & 0 deletions
142
...ary/collections/src/commonMain/kotlin/io/matthewnelson/immutable/collections/Immutable.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* | ||
* Copyright (c) 2024 Matthew Nelson | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
**/ | ||
@file:JvmName("Immutable") | ||
|
||
package io.matthewnelson.immutable.collections | ||
|
||
import kotlin.jvm.JvmName | ||
|
||
@JvmName("listOf") | ||
public fun <T> Collection<T>.toImmutableList(): List<T> { | ||
if (isEmpty()) return emptyList() | ||
if (this is ImmutableList<T>) return this | ||
return ImmutableList(toList()) | ||
} | ||
|
||
@JvmName("listOf") | ||
public fun <T> immutableListOf(vararg elements: T): List<T> { | ||
if (elements.isEmpty()) return emptyList() | ||
return ImmutableList(elements.toList()) | ||
} | ||
|
||
@JvmName("mapOf") | ||
public fun <K, V> Map<K, V>.toImmutableMap(): Map<K, V> { | ||
if (isEmpty()) return emptyMap() | ||
if (this is ImmutableMap<K, V>) return this | ||
return ImmutableMap(toMap()) | ||
} | ||
|
||
@JvmName("mapOf") | ||
public fun <K, V> immutableMapOf(vararg pairs: Pair<K, V>): Map<K, V> { | ||
if (pairs.isEmpty()) return emptyMap() | ||
return ImmutableMap(pairs.toMap()) | ||
} | ||
|
||
@JvmName("setOf") | ||
public fun <T> Collection<T>.toImmutableSet(): Set<T> { | ||
if (isEmpty()) return emptySet() | ||
if (this is ImmutableSet<T>) return this | ||
return ImmutableSet(toSet()) | ||
} | ||
|
||
@JvmName("setOf") | ||
public fun <T> immutableSetOf(vararg elements: T): Set<T> { | ||
if (elements.isEmpty()) return emptySet() | ||
return ImmutableSet(elements.toSet()) | ||
} | ||
|
||
private open class ImmutableCollection<T, D: Collection<T>>( | ||
protected val delegate: D | ||
): Collection<T> { | ||
final override val size: Int get() = delegate.size | ||
final override fun isEmpty(): Boolean = delegate.isEmpty() | ||
final override operator fun iterator(): Iterator<T> = ImmutableIterator(delegate.iterator()) | ||
final override fun containsAll(elements: Collection<T>): Boolean = delegate.containsAll(elements) | ||
final override operator fun contains(element: T): Boolean = delegate.contains(element) | ||
|
||
final override fun equals(other: Any?): Boolean = delegate == other | ||
final override fun hashCode(): Int = delegate.hashCode() | ||
final override fun toString(): String = delegate.toString() | ||
} | ||
|
||
private class ImmutableList<T>( | ||
delegate: List<T> | ||
): ImmutableCollection<T, List<T>>(delegate), List<T> { | ||
override operator fun get(index: Int): T = delegate[index] | ||
override fun indexOf(element: T): Int = delegate.indexOf(element) | ||
override fun lastIndexOf(element: T): Int = delegate.lastIndexOf(element) | ||
override fun listIterator(): ListIterator<T> = ImmutableListIterator(delegate.listIterator()) | ||
override fun listIterator(index: Int): ListIterator<T> = ImmutableListIterator(delegate.listIterator(index)) | ||
override fun subList(fromIndex: Int, toIndex: Int): List<T> = delegate.subList(fromIndex, toIndex).toImmutableList() | ||
} | ||
|
||
private class ImmutableSet<T>( | ||
delegate: Set<T> | ||
): ImmutableCollection<T, Set<T>>(delegate), Set<T> | ||
|
||
private class ImmutableMap<K, V>( | ||
private val delegate: Map<K, V> | ||
): Map<K, V> { | ||
|
||
override val entries: Set<Map.Entry<K, V>> by lazy { | ||
val entries = delegate.entries | ||
val set = LinkedHashSet<ImmutableMapEntry<K, V>>(entries.size, 1.0F) | ||
entries.mapTo(set) { ImmutableMapEntry(it) } | ||
ImmutableSet(set) | ||
} | ||
override val keys: Set<K> by lazy { ImmutableSet(delegate.keys) } | ||
override val size: Int get() = delegate.size | ||
override val values: Collection<V> by lazy { ImmutableCollection(delegate.values) } | ||
override fun isEmpty(): Boolean = delegate.isEmpty() | ||
override operator fun get(key: K): V? = delegate[key] | ||
override fun containsValue(value: V): Boolean = delegate.containsValue(value) | ||
override fun containsKey(key: K): Boolean = delegate.containsKey(key) | ||
|
||
override fun equals(other: Any?): Boolean = delegate == other | ||
override fun hashCode(): Int = delegate.hashCode() | ||
override fun toString(): String = delegate.toString() | ||
} | ||
|
||
private class ImmutableMapEntry<K, V>( | ||
private val delegate: Map.Entry<K, V>, | ||
): Map.Entry<K, V> { | ||
override val key: K get() = delegate.key | ||
override val value: V get() = delegate.value | ||
|
||
override fun equals(other: Any?): Boolean = delegate == other | ||
override fun hashCode(): Int = delegate.hashCode() | ||
override fun toString(): String = delegate.toString() | ||
} | ||
|
||
private open class ImmutableIterator<T, D: Iterator<T>>( | ||
protected val delegate: D | ||
): Iterator<T> { | ||
final override operator fun hasNext(): Boolean = delegate.hasNext() | ||
final override operator fun next(): T = delegate.next() | ||
|
||
final override fun equals(other: Any?): Boolean = delegate == other | ||
final override fun hashCode(): Int = delegate.hashCode() | ||
final override fun toString(): String = delegate.toString() | ||
} | ||
|
||
private class ImmutableListIterator<T>( | ||
delegate: ListIterator<T> | ||
): ImmutableIterator<T, ListIterator<T>>(delegate), ListIterator<T> { | ||
override fun hasPrevious(): Boolean = delegate.hasPrevious() | ||
override fun nextIndex(): Int = delegate.nextIndex() | ||
override fun previous(): T = delegate.previous() | ||
override fun previousIndex(): Int = delegate.previousIndex() | ||
} |
91 changes: 91 additions & 0 deletions
91
...ons/src/commonTest/kotlin/io/matthewnelson/immutable/collections/ImmutableListUnitTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
* Copyright (c) 2024 Matthew Nelson | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
**/ | ||
package io.matthewnelson.immutable.collections | ||
|
||
import kotlin.test.Test | ||
import kotlin.test.assertContentEquals | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertNotEquals | ||
|
||
class ImmutableListUnitTest { | ||
|
||
private val list = listOf("Hello", "World") | ||
|
||
@Test | ||
fun givenList_whenToImmutableList_thenReturnsImmutableList() { | ||
assertNotEquals("ImmutableList", list::class.simpleName) | ||
assertEquals("ImmutableList", list.toImmutableList()::class.simpleName) | ||
assertEquals("ImmutableList", immutableListOf("Hello", "World")::class.simpleName) | ||
} | ||
|
||
@Test | ||
fun givenEmpty_whenToImmutableList_thenReturnsEmptyList() { | ||
assertEquals("EmptyList", listOf<String>().toImmutableList()::class.simpleName) | ||
assertEquals("EmptyList", immutableListOf<String>()::class.simpleName) | ||
} | ||
|
||
@Test | ||
fun givenImmutableList_whenListIterator_thenReturnsImmutableIterators() { | ||
assertEquals("ImmutableListIterator", list.toImmutableList().listIterator()::class.simpleName) | ||
assertEquals("ImmutableListIterator", list.toImmutableList().listIterator(index = 1)::class.simpleName) | ||
} | ||
|
||
@Test | ||
fun givenList_whenToImmutableList_thenInitialListIsCopied() { | ||
val mutable = list.toMutableList() | ||
val immutable = mutable.toImmutableList() | ||
assertContentEquals(mutable, immutable) | ||
assertEquals(mutable, immutable) | ||
|
||
mutable.add("Something") | ||
assertNotEquals(mutable, immutable) | ||
} | ||
|
||
@Test | ||
fun givenImmutableList_whenSubList_thenReturnsImmutableList() { | ||
assertEquals("ImmutableList", list.toImmutableList().subList(0, 1)::class.simpleName) | ||
assertEquals("EmptyList", list.toImmutableList().subList(0, 0)::class.simpleName) | ||
} | ||
|
||
// ImmutableCollection | ||
@Test | ||
fun givenImmutableList_whenIterator_thenReturnsImmutableIterator() { | ||
assertEquals("ImmutableIterator", list.toImmutableList().iterator()::class.simpleName) | ||
} | ||
|
||
// ImmutableCollection | ||
@Test | ||
fun givenImmutableList_whenEqualsHashCodeToString_thenIsSameAsUnderlying() { | ||
assertEquals(list, list.toImmutableList()) | ||
assertContentEquals(list, list.toImmutableList()) | ||
assertEquals(list.hashCode(), list.toImmutableList().hashCode()) | ||
assertEquals(list.toString(), list.toImmutableList().toString()) | ||
} | ||
|
||
// ImmutableCollection | ||
@Test | ||
fun givenImmutableList_whenSize_thenIsSameAsUnderlying() { | ||
assertEquals(list.size, list.toImmutableList().size) | ||
} | ||
|
||
// ImmutableCollection | ||
@Test | ||
fun givenImmutableList_whenContainsAll_thenIsSameAsUnderlying() { | ||
assertEquals(list.containsAll(list), list.toImmutableList().containsAll(list)) | ||
assertEquals(list.containsAll(listOf("false")), list.toImmutableList().containsAll(listOf("false"))) | ||
assertEquals(list.contains("false"), list.toImmutableList().contains("false")) | ||
} | ||
} |
88 changes: 88 additions & 0 deletions
88
...ions/src/commonTest/kotlin/io/matthewnelson/immutable/collections/ImmutableMapUnitTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* Copyright (c) 2024 Matthew Nelson | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
**/ | ||
package io.matthewnelson.immutable.collections | ||
|
||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertNotEquals | ||
|
||
class ImmutableMapUnitTest { | ||
|
||
private val map = mapOf("Hello" to "World", "H" to "W") | ||
|
||
@Test | ||
fun givenMap_whenToImmutableMap_thenReturnsImmutableMap() { | ||
assertNotEquals("ImmutableMap", map::class.simpleName) | ||
assertEquals("ImmutableMap", map.toImmutableMap()::class.simpleName) | ||
assertEquals("ImmutableMap", immutableMapOf("Hello" to "World")::class.simpleName) | ||
} | ||
|
||
@Test | ||
fun givenEmpty_whenToImmutableMap_thenReturnsEmptyMap() { | ||
assertEquals("EmptyMap", mapOf<String, String>().toImmutableMap()::class.simpleName) | ||
assertEquals("EmptyMap", immutableMapOf<String, String>()::class.simpleName) | ||
} | ||
|
||
@Test | ||
fun givenMap_whenToImmutableMap_thenInitialMapIsCopied() { | ||
val mutable = map.toMutableMap() | ||
val immutable = mutable.toImmutableMap() | ||
assertEquals(mutable, immutable) | ||
mutable["aaa"] = "bbb" | ||
|
||
assertNotEquals(mutable, immutable) | ||
} | ||
|
||
@Test | ||
fun givenImmutableMap_whenEntries_thenAreImmutable() { | ||
val entries = map.toImmutableMap().entries | ||
assertEquals("ImmutableSet", entries::class.simpleName) | ||
assertEquals("ImmutableMapEntry", entries.first()::class.simpleName) | ||
assertEquals(map.entries, entries) | ||
} | ||
|
||
@Test | ||
fun givenImmutableMap_whenKeys_thenAreImmutable() { | ||
assertEquals("ImmutableSet", map.toImmutableMap().keys::class.simpleName) | ||
} | ||
|
||
@Test | ||
fun givenImmutableMap_whenValues_thenAreImmutable() { | ||
assertEquals("ImmutableCollection", map.toImmutableMap().values::class.simpleName) | ||
} | ||
|
||
@Test | ||
fun givenImmutableMap_whenEqualsHashCodeToString_thenIsSameAsUnderlying() { | ||
assertEquals(map, map.toImmutableMap()) | ||
assertEquals(map.hashCode(), map.toImmutableMap().hashCode()) | ||
assertEquals(map.toString(), map.toImmutableMap().toString()) | ||
} | ||
|
||
@Test | ||
fun givenImmutableMap_whenSize_thenIsSameAsUnderlying() { | ||
assertEquals(map.size, map.toImmutableMap().size) | ||
} | ||
|
||
@Test | ||
fun givenImmutableMap_whenContainsKey_thenIsSameAsUnderlying() { | ||
assertEquals(map.containsKey("Hello"), map.toImmutableMap().containsKey("Hello")) | ||
} | ||
|
||
@Test | ||
fun givenImmutableMap_whenContainsValue_thenIsSameAsUnderlying() { | ||
assertEquals(map.containsKey("World"), map.toImmutableMap().containsKey("World")) | ||
} | ||
} |
Oops, something went wrong.