如何从集合中删除重复项(不在中间创建新的重复项)?

时间:2014-12-29 10:22:28

标签: scala

首先,我完全意识到突变是一个坏主意,但我需要将对象创建降至最低,因为我需要处理大量的数据(保持GC挂起时间和速度)我的代码)。

我想要的是一个scala集合,它有一个像distinct或类似的方法,或者可能是一个库或代码片段(但首选的是原生scala),这样该方法可以副作用/改变集合而不是创建一个新的集合

我已经探讨了ArrayBuffermutable.ListArrayMutableListVector这些常见的嫌疑人,他们都是"创建一个新的序列"从原来而不是改变原来的位置。我想找到一些不存在的东西吗?我只需要写自己的吗?

我认为这存在于C ++ http://www.cplusplus.com/reference/algorithm/unique/

此外,如果存在某种令人敬畏的尾部递归方式,这样做,以便创建的任何簿记结构都保存在单个堆栈帧中,因此一旦该方法退出,就会从内存中释放出来。之所以这样,即使该方法创建了一些事物实例以执行重复删除,这些实例也不需要进行垃圾回收,因此不会导致大量的GC挂起。它不必是递归,只要它可能导致对象进入堆栈(参见此处的转义分析http://www.ibm.com/developerworks/java/library/j-jtp09275/index.html

(如果我可以指定并修复集合中的容量(内存大小)也很棒)

2 个答案:

答案 0 :(得分:2)

您提到的算法(针对C ++)适用于连续重复项。因此,如果连续需要它,可以使用一些LinkedList,但不推荐使用可变列表。另一方面,如果你想要一些内存有效并且同意线性访问 - 你可以用不同的迭代器(O(N))包装你的集合(可变或不可变):

def toConsDist[T](c: Traversable[T]) = new Iterator[T] {
    val i = c.toIterator
    var prev: Option[T] = None
    var _nxt: Option[T] = None
    def nxt = { 
       if (_nxt.isEmpty) _nxt = i.find(x => !prev.toList.contains(x))
       prev = _nxt
       _nxt 
    }        
    def hasNext = nxt.nonEmpty
    def next = {
       val next = nxt.get
       _nxt = None
       next
    }     
 }

 scala> toConsDist(List(1,1,1,2,2,3,3,3,2,2)).toList
 res44: List[Int] = List(1, 2, 3, 2)

如果您需要删除所有重复项,它将是О(N*N),但您不能使用scala集合,因为https://github.com/scala/scala/commit/3cc99d7b4aa43b1b06cc837a55665896993235fc(请参阅LinkedList部分),https://stackoverflow.com/a/27645224/1809978

但您可以使用Java的LinkedList

import scala.collection.JavaConverters._

scala> val mlist = new java.util.LinkedList[Integer]
mlist: java.util.LinkedList[Integer] = []

scala> mlist.asScala ++= List(1,1,1,2,2,3,3,3,2,2)
res74: scala.collection.mutable.Buffer[Integer] = Buffer(1, 1, 1, 2, 2, 3, 3, 3, 2, 2)

scala> var i = 0
i: Int = 0

scala> for(x <- mlist.asScala){ if (mlist.indexOf(x) != i) mlist.set(i, null); i+=1} //O(N*N)

scala> while(mlist.remove(null)){} //O(N*N)

scala> mlist
res77: java.util.LinkedList[Integer] = [1, 2, 3]

mlist.asScala只是创建包装而不进行任何复制。你不能在迭代期间修改Java的LinkedList,这就是我使用null的原因。您可以尝试使用Java ConcurrentLinkedQueue,但它不支持indexOf,因此您必须自己实现它(scala将其映射到Iterator,因此asScala.indexOf赢了不行。)

答案 1 :(得分:1)

根据定义,不变性会强制您在想要更改集合时创建新对象。

Scala为某些集合提供的是 buffers ,它允许您使用可变接口构建集合并最终返回不可变版本但是一旦获得了不可变集合,您就无法更改其以任何方式引用,包括过滤为不同。关于不可变集合中的可变性,你可以达到的最远点是当它们是可变对象时改变它的元素状态。

另一方面,一些集合Vector被实现为树(在本例中为trie),插入或删除操作不是通过复制整个树而只是复制所需的分支来实现的。

来自Martin Ordesky的 Scala编程

  

可以通过复制来更新向量中间的元素   包含该元素的节点以及指向该元素的每个节点,   从树的根开始。这意味着一个功能   update创建一到五个节点,每个节点最多包含32个节点   元素或子树。这肯定比一个更贵   可变数组中的就地更新,但仍然比它便宜很多   复制整个矢量。

相关问题