• Scala / Java - Guava Sets 高效实践


    一.引言

    使用 Scala 时经常需要集合的相关操作,最基本的就是交集、并集偶尔也会用到差集,博主在线上场景中使用 Scala 原生 Sets 的交集 intersect、并集 union 以及差集 diff 方法发现效率比较低,于是找到了 Google Guava 库下的高效 Sets 库 com.google.common.collect.Sets,下面看下二者的使用与效率。

    首先初始化两个 Sets 用于后续的测试使用:

    1. val random = new Random()
    2. val testSet1: mutable.Set[Int] = collection.mutable.Set.apply((0 to 10)
    3. .map(_ => random.nextInt(10)).toArray:_*)
    4. val testSet2: mutable.Set[Int] = collection.mutable.Set.apply((0 to 8)
    5. .map(_ => random.nextInt(8)).toArray:_*)

    这里用到了 apply 和 Scala 变长参数 :_*,更多使用可以参考:Scala 变长参数之*与:_* 。

    Tips:

    1. <dependency>
    2. <groupId>com.google.guavagroupId>
    3. <artifactId>guavaartifactId>
    4. <version>23.0version>
    5. <scope>providedscope>
    6. dependency>

    由于 Google Guava 库基于 Java 开发,所以对应的 Set 类型为 java.util.Set,如果使用 Java 不受影响,使用 Scala 则需要使用 JavaConverters 或 JavaConvertions 对 Scala Collection 进行转换,使其类型为 java.util.Sets,多种转换方法可以参考:Wrappers$MutableSetWrapper

    二.交集

    1. /**
    2. * @Describe Scala / Guava 交集对比
    3. * @params [arr1, arr2, format]
    4. * @return void
    5. * @author DDD
    6. * @since 2022/10/19 11:05 上午
    7. */
    8. def intersectionTest(set1: mutable.Set[Int], set2: mutable.Set[Int], format: String): Unit = {
    9. val st = System.currentTimeMillis()
    10. var count = 0
    11. while (count < epoch) {
    12. val re = if (format.equals("ori")) {
    13. set1.intersect(set2)
    14. } else {
    15. val utilSet1 = JavaConverters.mutableSetAsJavaSetConverter(set1).asJava
    16. val utilSet2 = JavaConverters.mutableSetAsJavaSetConverter(set2).asJava
    17. Sets.intersection(utilSet1, utilSet2)
    18. }
    19. if (count == 1) {
    20. println(s"Format: $format $re")
    21. }
    22. count += 1
    23. }
    24. val cost = System.currentTimeMillis() - st
    25. val log = s"Format: $format Epoch: $epoch Cost: $cost Mean: ${cost.toDouble / epoch}"
    26. println(log)
    27. }

    ori 模式使用 Scala 原生 Set 交集方法 intersect,guava 使用 Sets.intersection 方法:

    1. // 交集测试
    2. intersectionTest(testSet1, testSet2, "ori")
    3. intersectionTest(testSet1, testSet2, "guava")
    1. =================================================
    2. Format: ori Set(1, 2, 6, 7, 4)
    3. Format: ori Epoch: 10000 Cost: 83 Mean: 0.0083
    4. Format: guava [1, 2, 6, 7, 4]
    5. Format: guava Epoch: 10000 Cost: 33 Mean: 0.0033
    6. =================================================

    分别调用上述方法 10000 次保证足够大的实验样本,实践 Guava 速度是原生的 2 倍多。

    三.并集

    1. /**
    2. * @Describe Scala / Guava 并集对比
    3. * @params [arr1, arr2, format]
    4. * @return void
    5. * @author DDD
    6. * @since 2022/10/19 11:05 上午
    7. */
    8. def unionTest(set1: mutable.Set[Int], set2: mutable.Set[Int], format: String): Unit = {
    9. val st = System.currentTimeMillis()
    10. var count = 0
    11. while (count < epoch) {
    12. val re = if (format.equals("ori")) {
    13. set1.union(set2)
    14. } else {
    15. val utilSet1 = JavaConverters.mutableSetAsJavaSetConverter(set1).asJava
    16. val utilSet2 = JavaConverters.mutableSetAsJavaSetConverter(set2).asJava
    17. Sets.union(utilSet1, utilSet2)
    18. }
    19. if (count == 1) {
    20. println(s"Format: $format $re")
    21. }
    22. count += 1
    23. }
    24. val cost = System.currentTimeMillis() - st
    25. val log = s"Format: $format Epoch: $epoch Cost: $cost Mean: ${cost.toDouble / epoch}"
    26. println(log)
    27. }

    同理,这里使用原生的 union 和 Sets.union 进行对比:

    1. // 并集测试
    2. unionTest(testSet1, testSet2, "ori")
    3. unionTest(testSet1, testSet2, "guava")
    1. =================================================
    2. Format: ori Set(0, 9, 1, 5, 2, 6, 3, 7, 4)
    3. Format: ori Epoch: 10000 Cost: 22 Mean: 0.0022
    4. Format: guava [0, 9, 1, 5, 2, 6, 7, 4, 3]
    5. Format: guava Epoch: 10000 Cost: 6 Mean: 6.0E-4
    6. =================================================

    这次原生与 Guava 的差距扩大到 3 倍多。

    四.差集

    1. /**
    2. * @Describe Scala / Guava 并集对比
    3. * @params [arr1, arr2, format]
    4. * @return void
    5. * @author DDD
    6. * @since 2022/10/19 11:05 上午
    7. */
    8. def diffTest(set1: mutable.Set[Int], set2: mutable.Set[Int], format: String): Unit = {
    9. val st = System.currentTimeMillis()
    10. var count = 0
    11. while (count < epoch) {
    12. val re = if (format.equals("ori")) {
    13. set1.diff(set2)
    14. } else {
    15. val utilSet1 = JavaConverters.mutableSetAsJavaSetConverter(set1).asJava
    16. val utilSet2 = JavaConverters.mutableSetAsJavaSetConverter(set2).asJava
    17. Sets.difference(utilSet1, utilSet2)
    18. }
    19. if (count == 1) {
    20. println(s"Format: $format $re")
    21. }
    22. count += 1
    23. }
    24. val cost = System.currentTimeMillis() - st
    25. val log = s"Format: $format Epoch: $epoch Cost: $cost Mean: ${cost.toDouble / epoch}"
    26. println(log)
    27. }

    相比并集、交集,差集的使用可能相对较少,下面比较下效率:

    1. // 差集测试
    2. diffTest(testSet1, testSet2, "ori")
    3. diffTest(testSet1, testSet2, "guava")
    1. =================================================
    2. Format: ori Set(0, 9, 5)
    3. Format: ori Epoch: 10000 Cost: 18 Mean: 0.0018
    4. Format: guava [0, 9, 5]
    5. Format: guava Epoch: 10000 Cost: 5 Mean: 5.0E-4
    6. =================================================

    同样效率相差 3 倍多。

    五.总结

    上面测试的 mutable.Sets 的 Size 大约在 10 左右,为了实验的严谨性,我们将 Sets Size 修改为 1000 左右再次测试:

    1. val random = new Random()
    2. val testSet1: mutable.Set[Int] = collection.mutable.Set.apply((0 to 1000)
    3. .map(_ => random.nextInt(1000)).toArray:_*)
    4. val testSet2: mutable.Set[Int] = collection.mutable.Set.apply((0 to 800)
    5. .map(_ => random.nextInt(800)).toArray:_*)
    1. =================================================
    2. // 交集
    3. Format: ori Epoch: 10000 Cost: 301 Mean: 0.0301
    4. Format: guava Epoch: 10000 Cost: 40 Mean: 0.004
    5. =================================================
    6. // 并集
    7. Format: ori Epoch: 10000 Cost: 381 Mean: 0.0381
    8. Format: guava Epoch: 10000 Cost: 18 Mean: 0.0018
    9. =================================================
    10. // 差集
    11. Format: ori Epoch: 10000 Cost: 380 Mean: 0.038
    12. Format: guava Epoch: 10000 Cost: 13 Mean: 0.0013
    13. =================================================

    随着 Sets Size 的扩大,可以看到二者的效率差异也是肉眼可见的大,除此之外,由于 Scala Sets 需要转换至 java.util.Sets 同样需要时间,所以如果直接使用 java.util.Sets,运行效率会更进一步。

  • 相关阅读:
    紫光展锐6nm国产5G处理器T820_国产手机芯片5G方案
    神经网络基础篇:Python 中的广播(Broadcasting in Python)
    性能优化:编译器优化选项 -O2/-O3 究竟有多强大?
    leetcode 22. 括号生成
    032-从零搭建微服务-定时服务(一)
    Mysql数据类型
    毕业季,既是告别,也是新的开始
    Notepad++下载安装
    NLP(2)--Transformer
    将HTML页面中的table表格元素转换为矩形,计算出每个单元格的宽高以及左上角坐标点,输出为json数据
  • 原文地址:https://blog.csdn.net/BIT_666/article/details/127421098