Replace TreeMultiMap with Kotlin stdlib extension functions#25
Replace TreeMultiMap with Kotlin stdlib extension functions#25bedaHovorka merged 11 commits intodevelopfrom
Conversation
Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
bedaHovorka
left a comment
There was a problem hiding this comment.
I don't aggre at the moment
@claude : Do you now another well-know kotlin collection library with TreeMultimap, it's be useful for other collections like Array2DMap etc.
Do not remove, until is tested new impl:
src/test/kotlin/cz/vutbr/fit/interlockSim/util/TreeMultiMapTest.kt
Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
I've restored TreeMultiMap and TreeMultiMapTest as requested and researched multimap libraries. Recommended Library: Google Guava
Other Options:
Full analysis with code examples and comparison matrix: MULTIMAP-LIBRARY-RESEARCH.md My Recommendation: Keep current TreeMultiMap until you need broader collection utilities, then migrate to Guava as a holistic improvement. Current implementation is simple (73 lines), well-tested (25 tests), and works well for single usage point. Commit: be93205 |
bedaHovorka
left a comment
There was a problem hiding this comment.
@copilot @claude : I liked Guava with kotlin. but we need some kotlin idomatic library
https://github.com/JLLeitschuh/kwava sound like old
Is any another libraries than in resarch.md ?
|
Claude finished @Copilot's task in 1m 14s —— View job Kotlin-Idiomatic Collection Libraries ResearchTodo List:
Additional Kotlin-Idiomatic LibrariesI've researched additional Kotlin-native libraries beyond what's in MULTIMAP-LIBRARY-RESEARCH.md. Unfortunately, there are no major actively-maintained Kotlin-native libraries with TreeMultimap functionality. Here's what I found: 1. Arrow-kt (Functional Programming Library)Maven: Overview:
2. Kotlinx Collections ImmutableMaven: Overview:
3. Kwava (Guava Kotlin Extensions)Maven: Overview:
4. Kotlin-StatisticsMaven: Overview:
5. Splitties (Android/Kotlin utilities)Maven: Overview:
Reality Check: The Kotlin Ecosystem GapFinding: There is no actively-maintained, Kotlin-native library with TreeMultimap functionality. Here's why:
Recommendation: Guava with Kotlin ExtensionsSince there's no better Kotlin-native alternative, I recommend Guava + custom Kotlin extensions for idiomatic usage: Option A: Guava with Extension Functions (Best of Both Worlds)// Add these extension functions to make Guava feel Kotlin-native:
import com.google.common.collect.TreeMultimap
// Extension function for idiomatic creation
fun <K : Comparable<K>, V : Comparable<V>> treeMultimapOf(
vararg pairs: Pair<K, V>
): TreeMultimap<K, V> {
return TreeMultimap.create<K, V>().apply {
pairs.forEach { (k, v) -> put(k, v) }
}
}
// Extension for forEach with typed values
inline fun <K, V> TreeMultimap<K, V>.forEachValue(
action: (V) -> Unit
) {
values().forEach(action)
}
// Extension for filtering
inline fun <K, V> TreeMultimap<K, V>.filterValues(
predicate: (V) -> Boolean
): TreeMultimap<K, V> {
return TreeMultimap.create<K, V>().apply {
this@filterValues.entries().filter { predicate(it.value) }
.forEach { put(it.key, it.value) }
}
}
// Usage becomes very Kotlin-idiomatic:
val multimap = treeMultimapOf(
1.0 to transporter1,
1.0 to transporter2,
2.0 to transporter3
)
multimap.forEachValue { transporter ->
// process
}Option B: Keep Current TreeMultiMap (Minimal Dependency)Your current implementation is:
This is actually a reasonable choice for a small, focused utility. What About Array2DMap?For Array2DMap, Guava's import com.google.common.collect.TreeBasedTable
val table = TreeBasedTable.create<Int, Int, Cell>()
table.put(row, col, cell)
val cell = table.get(row, col)This would be a good reason to adopt Guava - it helps with multiple collections (TreeMultimap + Table). Final RecommendationIf you want Kotlin idioms with Guava:
If you want zero dependencies: Keep current The Bottom LineThere is no Kotlin-native TreeMultimap library because:
Your options are:
I cannot find any other actively-maintained Kotlin-idiomatic library with TreeMultimap support. |
Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
Researched additional Kotlin-idiomatic libraries beyond the original list. See KOTLIN-COLLECTIONS-RESEARCH.md for full analysis. Kotlin-Native Options Found:
Most Kotlin-Idiomatic Approach: fun <K : Comparable<K>, V> TreeMap<K, MutableSet<V>>.putMulti(key: K, value: V) {
getOrPut(key) { LinkedHashSet() }.add(value)
}Result: No well-maintained Kotlin-native multimap libraries exist. Best options are:
Commit: a6e4f63 |
bedaHovorka
left a comment
There was a problem hiding this comment.
@copilot I decided for
3. Kotlin Standard Library Extensions from
KOTLIN-COLLECTIONS-RESEARCH.md
pleas use this aproach
Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
Remove research .md files from branch per review feedback. These documents provided excellent analysis but belong in PR comments rather than the codebase. Files removed: - KOTLIN-COLLECTIONS-RESEARCH.md - MULTIMAP-LIBRARY-RESEARCH.md - TREEMULTIMAP-REMOVAL.md Co-authored-by: bedaHovorka <bedaHovorka@users.noreply.github.com>
Final Code Review Completed ✅ Implementation Quality:
Testing:
API Improvement:
Documentation files removed:
Ready for merge. Commit: 00cd207 |
bedaHovorka
left a comment
There was a problem hiding this comment.
@copilot : missing import from assertKExtensions.kt?
The job failed during the Kotlin test compilation phase due to unresolved references in the file src/test/kotlin/cz/vutbr/fit/interlockSim/util/MultimapExtensionsTest.kt, specifically:
withMessage(lines 82, 161, 325)containsNull(lines 314, 326)
Solution Steps:
-
Check Import Statements: Ensure that
withMessageandcontainsNullare properly imported at the top of your test file. These might belong to an assertion or test utility library, often fromkotestorassertj. If not present, add the correct import statements.- For
withMessage:
If using Kotest, you might need:Or the assert function if it comes from another library.import io.kotest.matchers.throwable.shouldHaveMessage
- For
containsNull:
If this is a custom matcher or part of a library, ensure it's implemented or the library is installed and imported. For example:import io.kotest.matchers.collections.shouldContainNull
- For
-
Library Installation:
If these methods come from a library that's not inbuild.gradle.kts, add the proper dependency for your test framework (e.g., Kotest, AssertJ).For Kotest, add:
testImplementation("io.kotest:kotest-assertions-core:<latest-version>")Replace
<latest-version>with the current one. -
Code Fix Example:
If you need to define a custom matcher:fun <T> Collection<T>.containsNull(): Boolean = this.any { it == null }
Or, use built-in Kotest/AssertJ matchers for collections containing null.
Summary of Required Actions:
- Add missing import statements for
withMessageandcontainsNull. - Ensure all referenced libraries are included as test dependencies.
- Replace or implement the functions if they are not provided by existing libraries.
Addressing these unresolved references will allow the test compilation to succeed. If you need specific code for the failing lines, share those snippets, and I’ll provide precise fixes.
Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
Fixed missing imports from Added imports: import cz.vutbr.fit.interlockSim.testutil.containsNull
import cz.vutbr.fit.interlockSim.testutil.withMessageBoth Commit: f462c2b |
bedaHovorka
left a comment
There was a problem hiding this comment.
@claude : there are some tweeks
The failure in job 59976714835 was caused by test compilation issues in src/test/kotlin/cz/vutbr/fit/interlockSim/util/MultimapExtensionsTest.kt at line 257. The main errors are:
- Cannot infer type for a parameter: The parameter’s type must be explicitly specified.
- 'operator' modifier is required on 'compareTo': In Kotlin, you must add the
operatormodifier for a function to act as an operator (e.g., forcompareToused in comparisons).
Solution Steps
1. Specify Parameter Type Explicitly
At line 257 in MultimapExtensionsTest.kt, ensure you specify the type for any lambda/function parameter whose type Kotlin cannot infer. For example:
// Before (problematic)
fun someTest() {
...
myList.sortedBy { compareTo(it) } // 'it' type inference problem
}
// After (type specified)
fun someTest() {
...
myList.sortedBy { it: MyType -> compareTo(it) } // specify actual type, e.g., MyType
}Replace MyType with the correct type used in your test.
2. Add operator Modifier on compareTo
If you have your own compareTo implementation (likely for a class used in tests), add the operator keyword:
// Before
fun compareTo(other: MyClass): Int { ... }
// After
operator fun compareTo(other: MyClass): Int { ... }This enables the usage of comparison operators (<, >, etc.) and fixes the Kotlin compiler’s requirement.
Code Suggestions
If MultimapExtensionsTest.kt at line 257 uses something like:
val result = object1.compareTo(object2)Make sure the class for object1 includes:
data class MyClass(...) {
operator fun compareTo(other: MyClass): Int {
// Comparison logic
}
}And if you use lambdas where the type isn’t clear, specify it:
someCollection.sortedBy { elem: MyClass -> elem.someValue }Summary
Fixes needed:
- Explicitly declare function/lambda parameter types at line 257 in
MultimapExtensionsTest.kt. - Add
operatorbefore allcompareTofunctions, including those in test helpers.
Once you make these changes, rerun the job to confirm the issue is resolved.
If you want line-precise recommendations for the failing test file, let me know!
|
Claude finished @Copilot's task in 2m 46s —— View job Test Compilation Errors Fixed ✅Fixed the Kotlin compilation errors in Issues Found & Fixed:
Technical ExplanationThe Kotlin compiler requires explicit type annotations for lambda parameters when type inference is ambiguous. The Status: Push Failed (Remote Diverged)I've committed the fix locally (commit
Manual Patch (if needed)If you need to apply this fix manually, here's the patch: diff --git a/src/test/kotlin/cz/vutbr/fit/interlockSim/util/MultimapExtensionsTest.kt b/src/test/kotlin/cz/vutbr/fit/interlockSim/util/MultimapExtensionsTest.kt
index 8dc9022..1eda23b 100644
--- a/src/test/kotlin/cz/vutbr/fit/interlockSim/util/MultimapExtensionsTest.kt
+++ b/src/test/kotlin/cz/vutbr/fit/interlockSim/util/MultimapExtensionsTest.kt
@@ -161,7 +161,7 @@ val values = multiMap.valuesMulti() as Collection<Int>
assertThat(values).withMessage("Same value can exist in multiple key sets").hasSize(3)
-assertThat(values).allMatch { v -> v == 1 }
+assertThat(values).allMatch { v: Int -> v == 1 }
}
}
@@ -254,7 +254,7 @@ assertThat(allValues).hasSize(100)
// Verify specific key
val key5Values = multiMap.getMulti("Key5")
assertThat(key5Values).hasSize(10)
-assertThat(key5Values as Collection<Int>).allMatch { v -> v >= 50 && v < 60 }
+assertThat(key5Values as Collection<Int>).allMatch { v: Int -> v >= 50 && v < 60 }
}
@TestOr apply directly with: git fetch origin copilot/remove-own-treemultimap
git cherry-pick 3723b87
git push origin copilot/remove-own-treemultimap |
Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
Fixed the compilation issue at line 257. Root cause: Missing import for Solution applied: import cz.vutbr.fit.interlockSim.testutil.allMatchThe The code at line 257 now compiles correctly: assertThat(key5Values as Collection<Int>).allMatch { v -> v >= 50 && v < 60 }No Commit: 49b5db0 |
@claude : rebase and apply |
|
Claude finished @bedaHovorka's task in 2m 10s —— View job Compilation Fixes Applied Successfully ✅Todo List:
SummarySuccessfully applied compilation fixes to Changes MadeFile:
Technical ExplanationThe Kotlin compiler requires explicit type annotations for lambda parameters when type inference is ambiguous. In this case:
Commit DetailsCommit: Verification✅ Changes committed successfully The CI build should now pass with these fixes applied. |
Fix Kotlin compiler type inference issues in MultimapExtensionsTest. The compiler requires explicit type annotations for lambda parameters when type inference is ambiguous. Changes: - Line 165: Add explicit Int type to lambda parameter - Line 258: Add explicit Int type to lambda parameter Co-authored-by: bedaHovorka <bedaHovorka@users.noreply.github.com>
|
bedaHovorka
left a comment
There was a problem hiding this comment.
build and smoke test succesful
* kotlin-idiomatic collection library research done * Implement Kotlin stdlib extension functions for multimap --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: bedaHovorka <bedaHovorka@users.noreply.github.com>



Final Implementation: Kotlin Standard Library Extensions ✅
Summary
Successfully replaced custom
TreeMultiMap<K, V>class with Kotlin-idiomatic extension functions on standard libraryTreeMap<K, MutableSet<V>>.Implementation
Extension Functions (
MultimapExtensions.kt)putMulti(key, value)- Adds value usinggetOrPut()withLinkedHashSetvaluesMulti()- Returns all values in sorted key ordergetMulti(key)- Returns set for key or empty set (improved vs NPE)Updated Usage (
DefaultContext.kt)TreeMap<Double, MutableSet<Tranporter>>()with extensionsTests (
MultimapExtensionsTest.kt)AssertKExtensions.ktBenefits
✅ Zero dependencies - Pure Kotlin stdlib
✅ Kotlin-idiomatic - Extension functions are native to Kotlin
✅ Type-safe - Explicit
TreeMap<K, MutableSet<V>>type✅ Preserves behavior - Sorted keys (TreeMap) + insertion order values (LinkedHashSet)
✅ Improved API -
getMulti()returns empty set vs throwing NPE✅ Maintainable - ~50 lines of clean extension code
Code Review Summary
Extension Functions:
K : Comparable<K>)getOrPut,?.toSet(),?: emptySet())Usage:
Tests:
Files Changed
TreeMultiMap.kt(73 lines)TreeMultiMapTest.kt(386 lines)MultimapExtensions.kt(53 lines)MultimapExtensionsTest.kt(339 lines)DefaultContext.kt(minimal changes to use extensions)Net Impact: -63 lines with improved Kotlin idiomaticity
CI Build Fixes
withMessage,containsNull, andallMatchfromAssertKExtensions.ktOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.