scala ArrayBuffer удаляет все элементы с предикатом - PullRequest
5 голосов
/ 05 апреля 2011

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 это будет медленно, потому что индексация занимает линейное время.

Ответы [ 6 ]

3 голосов
/ 05 апреля 2011

Я предполагаю, что более эффективно фильтровать путем создания нового буфера, поэтому вы обычно просто используете 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)
1 голос
/ 22 января 2018

Я придумал это

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)
        }
    }

}
1 голос
/ 05 апреля 2011

Вы делаете то же самое с ArrayBuffer. Все классы коллекции имеют одинаковые доступные методы.

1 голос
/ 05 апреля 2011

Были дискуссии о наличии набора методов, которые работают путем выполнения мутации, но придумать хороший общий набор на удивление сложно, и, с другой стороны, просто не было достаточного спроса на него.

0 голосов
/ 05 апреля 2011

Это сработало для меня, но только с 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)
0 голосов
/ 05 апреля 2011

Часто withFilter достаточно хорош, особенно если в конце буфер превращается в неизменную структуру.Правда, он не удаляет элементы, но, по крайней мере, не создает новый объект ArrayBuffer.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...