5

Scala 在过滤不可变序列方面非常优雅:

var l = List(1,2,3,4,5,6)
l = l.filter(_%2==1)

但是如何使用像 ArrayBuffer 这样的可变集合来做到这一点?我发现的只是删除单个元素或切片,或从另一个序列中删除元素,但没有删除谓词给出的元素。

编辑:我希望能找到类似的东西:

trait Removable[A] extends Buffer[A]{ 
  def removeIf(p: A => Boolean){
    var it1 = 0
    var it2 = 0

    while(it2 < length){
      if( p( this(it2) ) ){
        it2 += 1;
      } 
      else {
        this(it1) = this(it2)
        it1 += 1;
        it2 += 1;
      }
    }

    trimEnd(it2-it1)
  }
}

这个过滤器在线性时间内可以混合到任何缓冲区中,但只有 ArrayBuffer 才有意义,在 ListBuffers 上它会很慢,因为索引确实需要线性时间。

4

6 回答 6

3

我的猜测是,通过构建新缓冲区进行过滤会更有效,因此您通常只需使用filter并使用其结果。否则,您可以编写自己的就地过滤方法:

def filterInPlace[A](b: collection.mutable.Buffer[A])(fun: A => Boolean): Unit = {
  var sz = b.size
  var i = 0; while(i < sz) {
    if (fun(b(i))) {
      i += 1
    } else {
      sz -= 1
      b.remove(i)
    }
  }
}

val b = collection.mutable.ArrayBuffer((1 to 6): _ *)
filterInPlace(b)(_ % 2 == 1)
println(b)
于 2011-04-05T01:39:04.450 回答
1

你对ArrayBuffer. 所有集合类都有相同的可用方法。

于 2011-04-05T10:21:05.090 回答
1

已经讨论过拥有一组通过执行突变来工作的方法,但是想出一个好的通用集非常困难,而且另一方面,对它的需求还不够。

于 2011-04-05T02:10:58.357 回答
1

我想出了这个

import scala.collection.mutable

trait BufferUtils {
    import BufferUtils._
    implicit def extendMutableBuffer[T](org: mutable.Buffer[T]): ExtendedBuffer[T] = new ExtendedBuffer(org)
}

object BufferUtils extends BufferUtils {

    implicit class ExtendedBuffer[T](val org: mutable.Buffer[T]) extends AnyVal {
        def removeIf(pred: (T) => Boolean): Unit = {
            // target holds the index we want to move the next element to
            var target = 0

            for (i <- org.indices;
                 elem = org(i)
                 if !pred(elem)) {

                org(target) = elem
                target += 1
            }

            org.remove(target, org.size - target)
        }
    }

}
于 2018-01-22T13:14:04.690 回答
0

这对我有用,但仅适用于 clone(),因此仍然制作了一个新的 ArrayBuffer :-)

scala> import collection.mutable.ArrayBuffer
import collection.mutable.ArrayBuffer

scala> val buf = ArrayBuffer(1,2,3,4,5,6,7,8,9,10)
buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> buf.clone foreach { x => if (x > 4) buf -= x }

scala> buf
res1: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)

但更好的方法是创建一个仅包含您想要删除的元素的新数组(因此不复制整个缓冲区),然后删除它们:

scala> val buf = ArrayBuffer(1,2,3,4,5,6,7,8,9,10)
buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> buf filter { _ > 4 } foreach { buf -= _ }

scala> buf
res3: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
于 2011-04-05T07:17:57.053 回答
0

通常withFilter就足够了,尤其是当 Buffer 最终转换为不可变结构时。没错,它并没有真正删除元素,但至少它没有创建一个新的 ArrayBuffer 对象。

于 2011-04-05T06:25:43.477 回答