Kotlin 在获得最大值最佳方法之前过滤列表?

Kotlin is filtering list before getting max value best apporach?

如果你有一个对象列表 object(category, value) 并且想要获得不包括某些类别的最大值你可以使用这样的东西:

val max = objects.filter { it.category.name != xy }.maxByOrNull { it.value }

但是,如果我理解正确的话,它使用了 2 个迭代器,那么是否会有仅使用一个迭代器的此调用的性能更高的版本?

没错。此代码将首先遍历整个列表以过滤结果,然后再次查找最大值。

我将在下面详细介绍一些备选方案,但总的来说,如果没有很好的理由,我会反对其中任何一种。性能优势通常是微不足道的——花时间确保代码清晰且经过良好测试将是更好的投资。我建议坚持使用现有代码。

例子

这是您的代码的可执行版本。

fun main() {

  val items = listOf(
    MyData("shoes", 1),
    MyData("shoes", 22),
    MyData("shoes", 33),
    MyData("car", 555),
  )

  val max = items
    .filter {
      println("  filter $it")
      it.categoryName == "shoes"
    }.maxByOrNull {
      println("  maxByOrNull $it")
      it.value
    }

  println("\nresult: $max")
}

有两次迭代,每次都是运行两次。

  filter MyData(categoryName=shoes, value=1)
  filter MyData(categoryName=shoes, value=22)
  filter MyData(categoryName=shoes, value=33)
  filter MyData(categoryName=car, value=555)
  maxByOrNull MyData(categoryName=shoes, value=1)
  maxByOrNull MyData(categoryName=shoes, value=22)
  maxByOrNull MyData(categoryName=shoes, value=33)

result: MyData(categoryName=shoes, value=33)

序列

在某些情况下,您可以使用sequences来减少操作次数。

  val max2 = items
    .asSequence()
    .filter {
      println("  filter $it")
      it.categoryName == "shoes"
    }.maxByOrNull {
      println("  maxByOrNull $it")
      it.value
    }

  println("\nresult: $max2")

如你所见,操作顺序不同

  filter MyData(categoryName=shoes, value=1)
  filter MyData(categoryName=shoes, value=22)
  maxByOrNull MyData(categoryName=shoes, value=1)
  maxByOrNull MyData(categoryName=shoes, value=22)
  filter MyData(categoryName=shoes, value=33)
  maxByOrNull MyData(categoryName=shoes, value=33)
  filter MyData(categoryName=car, value=555)

result: MyData(categoryName=shoes, value=33)

[S]equences let you avoid building results of intermediate steps, therefore improving the performance of the whole collection processing chain.

请注意,在这个小示例中,收益不值得。

However, the lazy nature of sequences adds some overhead which may be significant when processing smaller collections or doing simpler computations.

组合操作

在您的小示例中,您可以组合 'filterr' 和 'maxBy' 操作

  val max3 = items.maxByOrNull { data ->
    when (data.categoryName) {
      "shoes" -> data.value
      "car"   -> -1
      else    -> -1
    }
  }
  println("\nresult: $max3")
result: MyData(categoryName=shoes, value=33)

我希望这个解决方案不是立即可以理解的,并且有一些令人讨厌的边缘情况,这将是错误的主要来源。我不会详细说明这些问题,而是 re-iterate ease-of-use、适应性和简单代码通常比优化代码更有价值!