| /* |
| * Copyright 2010-2026 JetBrains s.r.o. and Kotlin Programming Language contributors. |
| * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. |
| */ |
| |
| package test.collections |
| |
| import kotlin.random.Random |
| import kotlin.test.assertEquals |
| import kotlin.test.assertFalse |
| import kotlin.test.assertTrue |
| import kotlin.test.Test |
| |
| class MutableMapRemoveHashAtTest { |
| |
| // Reproducer from KT-82783 |
| @Test |
| fun buildMapDuplicatesReproducer() = testOnMaps { |
| put(148961824, 1) |
| put(148962400, 1) |
| put(148963552, 1) |
| put(148964704, 1) |
| put(148965856, 1) |
| put(148967008, 1) |
| put(148968160, 1) |
| put(148969312, 1) |
| put(148970464, 1) |
| put(148971616, 1) |
| put(148972768, 1) |
| put(148973920, 1) |
| put(148975072, 1) |
| put(148976224, 1) |
| put(148977376, 1) |
| put(148978528, 1) |
| remove(148961824) |
| put(148978528, 1) |
| } |
| |
| @Test |
| fun buildMapDuplicatesReproducerReduced() = testOnMaps { |
| put(59, 1) |
| put(38, 1) |
| put(67, 1) |
| put(54, 1) |
| put(70, 1) |
| put(44, 1) |
| put(65, 1) |
| remove(59) |
| put(65, 1) // it will be duplicated |
| } |
| |
| /** |
| * | hash | keys | |
| * +------+-----------+ |
| * | 0 | 0, 13 | |
| * | 1 | 5, 18, 26 | |
| * | 2 | 10, 31 | |
| * | 3 | 2, 23 | |
| * | 4 | 15, 28 | |
| * | 5 | 7, 20 | |
| * | 6 | 12, 33 | |
| * | 7 | 4, 25 | |
| * | 8 | 9, 17, 30 | |
| * | 9 | 1, 22 | |
| * | 10 | 14, 27 | |
| * | 11 | 6, 19 | |
| * | 12 | 11, 32 | |
| * | 13 | 3, 24 | |
| * | 14 | 16, 29 | |
| * | 15 | 8, 21 | |
| */ |
| @Test |
| fun buildMapDuplicatesMinimalProbeDistance() = testOnMaps { |
| put(4, 1) // hash == 7 |
| put(25, 1) // hash == 7 |
| put(12, 1) // hash == 6 |
| put(33, 1) // hash == 6 |
| put(7, 1) // hash == 5 |
| put(15, 1) // hash == 4 |
| put(2, 1) // hash == 3 |
| remove(4) |
| put(2, 1) |
| } |
| |
| @Test |
| fun removeHashAtReturnByProbeDistance() = testOnMaps { |
| put(7, 1) // hash == 5 |
| put(15, 1) // hash == 4 |
| put(2, 1) // hash == 3 |
| put(10, 1) // hash == 2 |
| remove(7) |
| |
| assertEquals(3, size) |
| assertFalse(containsKey(7)) |
| assertEquals(1, get(15)) |
| assertEquals(1, get(2)) |
| assertEquals(1, get(10)) |
| } |
| |
| @Test |
| fun removeHashAtStressTest() { |
| val random = Random(0xcafebabe) |
| repeat(50_000) { |
| testOnMaps { |
| val inserted = mutableSetOf<Int>() |
| |
| repeat(8) { |
| val key = random.nextInt(50) |
| put(key, key) |
| inserted.add(key) |
| } |
| |
| val toDelete = inserted.shuffled(random).take(random.nextInt(inserted.size)) |
| for (key in toDelete) { |
| remove(key) |
| inserted.remove(key) |
| } |
| |
| assertEquals(inserted.size, size) |
| for (key in inserted) { |
| assertEquals(key, get(key)) |
| } |
| |
| repeat(toDelete.size) { |
| val key = random.nextInt(50) |
| put(key, key) |
| } |
| } |
| } |
| } |
| |
| private inline fun testOnMaps(performOperations: MutableMap<Int, Int>.() -> Unit) { |
| val capacity = 8 |
| buildMap(capacity) { performOperations() }.assertNoDuplicateKeys() |
| HashMap<Int, Int>(capacity).apply { performOperations() }.assertNoDuplicateKeys() |
| LinkedHashMap<Int, Int>(capacity).apply { performOperations() }.assertNoDuplicateKeys() |
| } |
| |
| private fun Map<Int, Int>.assertNoDuplicateKeys() { |
| val uniqueKeys = HashSet(keys) |
| if (uniqueKeys.size != keys.size) { |
| val duplicates = keys.groupingBy { it }.eachCount().filterValues { it > 1 } |
| assertTrue(duplicates.isEmpty(), "Found duplicates: $duplicates") |
| } |
| } |
| } |