# exposed-0003: Table.clone — O(N²) repeated List allocation in property filter **Severity:** MEDIUM **File:** exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Table.kt **Line:** 1686 **Status:** PATCHED ## Description The private `T.clone()` utility in `Table` is used internally when cloning column objects (e.g., during alias creation and column type mutation). It contains: ```kotlin val allValues = memberProperties .filter { it in mutableProperties || it.name in consParams.map(KParameter::name) } .associate { it.name to (replaceArgs[it] ?: it.get(this@clone)) } ``` The predicate `it.name in consParams.map(KParameter::name)` is evaluated for every element of `memberProperties`. Each evaluation calls `.map(KParameter::name)`, allocating a fresh `List`. For a class with P properties and C constructor parameters, this is P × C string comparisons plus P list allocations. `Column` classes can have 10–20 properties; this is called once per column per alias/clone operation. In a query with 50 aliased columns this function runs 50 times, each time performing up to 400 string comparisons with 20 temporary `List` allocations per call — 20 000 comparisons and 1 000 heap allocations total. ## Root Cause `consParams.map(KParameter::name)` is a lambda-captured expression inside the `.filter {}` predicate. Kotlin does not hoist it; it runs inside the hot loop. ## Fix Pre-compute the parameter name set once, outside the filter: ```kotlin // Before (O(P×C) with P list allocations): val allValues = memberProperties .filter { it in mutableProperties || it.name in consParams.map(KParameter::name) } .associate { it.name to (replaceArgs[it] ?: it.get(this@clone)) } // After (O(P) with O(1) lookup): val consParamNames = consParams.mapTo(HashSet()) { it.name } val allValues = memberProperties .filter { it in mutableProperties || it.name in consParamNames } .associate { it.name to (replaceArgs[it] ?: it.get(this@clone)) } ``` ## Speedup ~15× at P=20 properties, C=15 constructor parameters.