kotlin mapof_Kotlin mapOf和mutableMapOf性能之间的权衡

kotlin mapof

Recently, when doing a small coding challenge in Kotlin I was faced with the choice of mutableMapOf() or HashMap()

最近,在Kotlin中进行小型编码挑战时,我面临选择mutableMapOf()HashMap()

Being a Java old-timer, I went with HashMap() .

作为Java的老手,我选择了HashMap()

But but… all the Kotlin tutorials start with mapOf() and mutableMapOf() ?

但是,但是...所有的Kotlin教程下手 mapOf() mutableMapOf()

Afterwards, I had some interesting discovery to share here:

之后,我在这里分享了一些有趣的发现:

Behind the scene, both mapOf and mutableMapOf use Java’s LinkedHashMap data structure

台前幕后,都mapOfmutableMapOf使用Java的LinkedHashMap的数据结构

Utimately, the comparison between mapOf, mutableMapOf() and HashMap() is down to LinkedHashMap vs. HashMap in Java.

Utimately,之间的比较mapOfmutableMapOf()HashMap()下降到Java中的LinkedHashMapHashMap中

These are the 2 most popular implementations of Map (or Dictionary or Hashtable if you will) data structure in Java. Here you can find a very detailed comparison between the too.

这是Java中Map (或字典或Hashtable)数据结构的2种最流行的实现。 在这里您也可以找到非常详细的比较。

The theory is that writing to a HashMap should be faster than a LinkedHashMap (as the former doesn’t care about maintaining insertion order). Also, the difference should be constant, not even linear.

从理论上讲,写入HashMap应该比LinkedHashMap更快(因为前者并不关心维护插入顺序)。 同样,差异应该是恒定的,甚至不是线性的。

Let’s measure them for fun anyway :)

让我们无论如何来衡量它们的乐趣:)

写性能对决mutableMapOf()vs HashMap() (Write performance showdown mutableMapOf() vs HashMap())

We’ll be using the following function to initialize 2 maps, one with HashMap() and the other with mutableMapOf()

我们将使用以下函数初始化2个地图,一个使用HashMap()初始化,另一个使用mutableMapOf()

fun compareMapInits(repeat: Int, size: Int) {
    println(">>> compareMapInits for $size elements (repeated $repeat times)")
    var totalMutableMapRuntime: Long = 0
    var totalHashMapRuntime : Long = 0
    var countHashMapSlower = 0
    repeat(repeat) {
        // mutableMapOf()
        val startTime = System.currentTimeMillis()
        val map = mutableMapOf<Int, Int>()
        (0..size).forEach {
            map[it] = it
        }
        val mutableMapRuntime = System.currentTimeMillis() - startTime
        totalMutableMapRuntime += mutableMapRuntime
        // HashMap()
        val startTime2 = System.currentTimeMillis()
        val map2 = HashMap<Int, Int>()
        (0..size).forEach {
            map2[it] = it
        }
        val hashMapRuntime = System.currentTimeMillis() - startTime2
        totalHashMapRuntime += hashMapRuntime


        if (hashMapRuntime > mutableMapRuntime) {
            countHashMapSlower++
        }
    }
    println("mutableMapOf() total time taken: $totalMutableMapRuntime ms")
    println("HashMap() total time taken: $totalHashMapRuntime ms")
    println("How often is HashMap() slower? ${countHashMapSlower}/$repeat")
    println("<<<")
}

一次运行 (One time run)

fun main() {
compareMapInits(1, 10)
compareMapInits(1, 100)
compareMapInits(1, 10_000)
compareMapInits(1, 1_000_000)
}

Results in

结果是

>>> compareMapInits for 10 elements (repeated 1 times)
- mutableMapOf: time taken: 3ms
- HashMap: time taken: 0ms
<<<
>>> compareMapInits for 100 elements (repeated 1 times)
- mutableMapOf: time taken: 0ms
- HashMap: time taken: 0ms
<<<
>>> compareMapInits for 10000 elements (repeated 1 times)
- mutableMapOf: time taken: 2ms
- HashMap: time taken: 1ms
<<<
>>> compareMapInits for 1000000 elements (repeated 1 times)
- mutableMapOf: time taken: 107ms
- HashMap: time taken: 119ms
<<<

What stands out here are:

这里突出的是:

  • For a small number of entries (10), mutableMapOf() takes more than 3 times to write compare to HashMap() ! But the difference is only 3ms in absolute terms. Let’s repeat the operation a number of times later as we might see a substantial difference.

    对于少量的条目(10),与HashMap()相比, mutableMapOf()写入时间要超过3倍! 但是绝对差只有3ms。 我们可能会看到很大的不同,让我们在以后重复操作多次。

  • For a large number of entries (1mil): mutableMapOf only takes longer by a few milliseconds compared to HashMap() most of the time. Interestingly, it’s sometimes faster than HashMap() . Let’s measure the probability below as well.

    对于大量条目(1百万):与大多数情况下相比,与HashMap()相比, mutableMapOf只需要花费几毫秒的时间。 有趣的是,它有时比HashMap()更快。 让我们也测量下面的概率。

重复运行 (Repetitive runs)

1k times:

一千次:

fun main() {
compareMapInits(1000, 10)
compareMapInits(1000, 100)
compareMapInits(1000, 10_000)
compareMapInits(1000, 1_000_000)
}

outputs

输出

>>> compareMapInits for 10 elements (repeated 1000 times)
mutableMapOf() total time taken: 5 ms
HashMap() total time taken: 2 ms
How often is HashMap() slower? 2/1000
<<<
>>> compareMapInits for 100 elements (repeated 1000 times)
mutableMapOf() total time taken: 8 ms
HashMap() total time taken: 10 ms
How often is HashMap() slower? 10/1000
<<<
>>> compareMapInits for 10000 elements (repeated 1000 times)
mutableMapOf() total time taken: 335 ms
HashMap() total time taken: 225 ms
How often is HashMap() slower? 206/1000
<<<
>>> compareMapInits for 1000000 elements (repeated 1000 times)
mutableMapOf() total time taken: 81266 ms
HashMap() total time taken: 69712 ms
How often is HashMap() slower? 180/1000
<<<
  • HashMap()write performance win increase significantly, in absolute terms when you repeat the operation many times.

    从绝对的角度来讲,当您多次重复操作时, HashMap()写入性能优势将大大提高。

  • Interestingly, when writing thousands of entries or more, there’s a ~20% chance HashMap() will be slower!

    有趣的是,当编写数千个或更多条目时,HashMap()会变慢20%!

结论 (Conclusion)

HashMap() write is faster than mutableMapOf() a.k.a. Java’s LinkedHashMap

HashMap()写入速度比mutableMapOf() (又名Java的LinkedHashMap mutableMapOf()

The difference is constant and insignificant when writing millions of elements.

编写数百万个元素时,差异是恒定的且微不足道的。

Unless you repeatedly write to maps many times, it’s probably not worth it using HashMap() over mutableMapOf() in production. since a wrong-order bug might be disastrous.

除非您多次重复编写地图,否则在生产环境中使用HashMap()而非mutableMapOf()可能不值得。 因为错误顺序的错误可能是灾难性的。

“Premature optimization is the root of all evil” — Donald Ervin Knuth

“过早的优化是万恶之源” —唐纳德·欧文·纳斯

On the other hand though, one should be careful about using ordered Map in production, IMHO. While it’s useful during a prototyping phase, over-reliance on Map data structure can be fragile and harmful for your production system in the long run. You never know what you’ll get from the API in a Map for example. Properly designed data classes should be preferred. For simple usage, List<KeyValuePair> can be a great choice IMO, as it explicitly states the importance of ordering here.

另一方面,恕我直言,在生产中使用有序Map应该要小心。 虽然在原型制作阶段它很有用,但从长远来看,对Map数据结构的过度依赖可能对您的生产系统脆弱且有害。 例如,您永远都不知道从Map中的API会获得什么。 正确设计的数据类应该是首选。 对于简单用法, List<KeyValuePair>可能是IMO的不错选择,因为它在此明确指出了订购的重要性。

What do you guys think?

你们有什么感想?

P.S. I wonder if mentioning the performance win for HashMap() vs. mutableMapOf() will gain you some bonus points in a data structure and algorithm interview though.

PS我不知道是否提及HashMap()mutableMapOf()的性能双赢会在数据结构和算法访谈中为您带来一些加分。

进一步阅读 (Further reading)

They are not the only implementation of Map of course. TreeHashMap is useful in certain scenarios for example.

当然,它们并不是Map的唯一实现。 例如,TreeHashMap在某些情况下很有用。

翻译自: https://medium.com/swlh/kotlin-mutable-map-write-performance-90bd14bc67b0

kotlin mapof

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值