【问题标题】:How to use ListBuffer in scala?如何在 scala 中使用 ListBuffer?
【发布时间】:2011-09-28 01:05:33
【问题描述】:

我有一种情况,我将单个元素列表传递给一个方法。在此方法中,列表中的单个元素递增 1。这样在方法调用之后,列表的第一个元素被修改(加一)。

代码是这样的:

val ct = List(5)

someMethod(ct)

println (ct(0))

// should print 6

......

//within somethod, I incrment the element like:
def someMethod(ct: List[Int}) {
    ct(0) = ct(0) + 1
}

当然,上面的代码在 Scala 中不起作用。我查看了ListBuffer,但我发现 scala 文档很难理解。 Scala 文档分为 2 组:类型成员和值成员。在类型成员中有类 WithFiler 和值成员有很多方法。我如何使用 WithFiler (可能与这个问题没有直接关系,但我想了解如何使用 scala doc)。

如果我想获得非常高的性能(someMethod 被调用数百万次),ListBuffer 似乎是解决这个问题的正确方法(如果我错了,请纠正我)。

如果 ListBuffer 是正确的列表类型,那么如何解决上述问题,如果不是,解决方案是什么?

【问题讨论】:

  • 这是两个不同的问题,真的——一个与使用 ListBuffer 有关,另一个与理解 Scaladoc 的类型成员和值成员有关。后一个问题请单独提出。

标签: scala


【解决方案1】:

在 scala 中,表达式:

ct(0) = ct(0) + 1

改写为:

ct.update( 0, ct.apply(0) + 1 )

没有为超类型List 定义方法update,因为列表可以是不可变的。但是,这是函数参数的类型。

所以你必须只使用 ListBuffers(或可变超类型):

def someMethod(ct: ListBuffer[Int]) {
  ct(0) = ct(0) + 1
}

scala> val lst = ListBuffer( 5 )
  lst: scala.collection.mutable.ListBuffer[Int] = ListBuffer(5)
scala> someMethod( lst )
scala> lst
  res2: scala.collection.mutable.ListBuffer[Int] = ListBuffer(6)

顺便说一句,如果您需要通过索引访问元素,请改用ArrayBuffer。它应该像 java ArrayList 一样工作。

最后,如果你不需要考虑WithFilter 的东西。只需使用filter 方法即可。

【讨论】:

    【解决方案2】:

    如果性能是主要目标并且已知集合的最大大小,则可以使用直接映射到 java 数组的 Array。

    【讨论】:

      【解决方案3】:

      这有一种过早的微优化的味道。

      尽管使用可变性(包括优化)有正当理由,但您没有说明为什么您认为您的使用是有效的,或者您要解决的更大问题。特别是,不可变列表在获取尾部和前置新头部时非常高效 - 100% 的非头部元素将在原始列表和新列表之间共享。

      如前所述,满足您要求的最干净的解决方案是忘记 ListBuffer,坚持使用不可变的 List 并实施 someMethod 而不会产生副作用。

      def someMethod(xs: List[Int]) = xs match {
        case h :: t => (h+1) :: t
        case Nil => Nil
      }
      
      val ct = List(5)
      println (someMethod(ct).headOption getOrElse "empty list")
      
      // should print 6
      

      另一方面,如果此性能热点,并且您无法通过更改算法确定任何提高性能的方法,那么您将需要使用Array

      Array 是 JVM 上唯一具体化的集合类型。因此,您可以直接使用 Array 中的原语,并避免其他集合类型必须应对的装箱/拆箱。

      这里的优势与可变性与不变性无关;拆箱/装箱的性能成本远远高于使用不可变列表的性能成本(如果有的话)。

      【讨论】:

        【解决方案4】:

        您当然可以重写上面的代码以使用ListBuffer,但如果您想要的只是单个Int 的引用调用语义,它不一定是最有效的解决方案。 ArrayBuffer 应该会好一点。你也可以这样做:

        class IntCell(var x: Int)
        def someMethod(ct: IntCell) {
          ct.x += 1
        }
        

        我通常会尝试重写我的代码,以避免在这类事情上使用可变集合。例如,您可以只返回新值:

        def someMethod(ct: Int) = ct + 1
        

        【讨论】:

          【解决方案5】:

          如果您想使用 ListBuffer,您只需将代码中的单词“List”替换为“ListBuffer”即可。但是,我会完全像 naten 建议的那样避免使用副作用函数(你的 someMethod: Unit)。

          ListBuffer 具有“高性能” - 然而,主要目的是添加项目/修改集合。如果我理解正确,您只需将一个项目集合中的项目更新一百万次 - 数组就足够了。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-10-26
            • 2020-02-16
            • 2017-03-22
            • 1970-01-01
            • 1970-01-01
            • 2014-04-11
            • 2019-03-02
            • 1970-01-01
            相关资源
            最近更新 更多