功能编程:列表是否仅包含唯一项?

tst*_*ner 10 language-agnostic functional-programming scala

我有一个未排序的列表,想要知道它中的所有项目是否都是唯一的.
我天真的做法是

val l = List(1,2,3,4,3)
def isUniqueList(l: List[Int]) = (new HashSet()++l).size == l.size

基本上,我正在检查包含列表中所有元素的Set是否具有相同的大小(因为在原始列表中出现两次的项目只会在集合中出现一次),但我不确定这是否是理想的解决方案对于这个问题.

编辑: 我的基准3个最流行的解决方案,l==l.distinct,l.size==l.distinct.size和阿列克谢的基于HashSet的解决方案.每个函数运行1000次,包含10个项目的唯一列表,10000个项目的唯一列表以及第三个季度中出现的一个项目复制到列表中间的相同列表.在每次运行之前,每个函数都被调用1000次来预热JIT,整个基准测试在使用System.currentTimeMillis之前运行了5次.这台机器是C2D P8400(2.26 GHz),内存为3GB,java版本是OpenJDK 64位服务器VM(1.6.0.20).java args是-Xmx1536M -Xms512M

结果:

l.size==l.distinct.size (3, 5471, 2, 6492)
l==l.distinct           (3, 5601, 2, 6054)
Alexey's HashSet        (2, 1590, 3, 781)

较大对象的结果(字符串从1KB到5KB):

l.size==l.distinct.size MutableList(4, 5566, 7, 6506)
l==l.distinct           MutableList(4, 5926, 3, 6075)
Alexey's HashSet        MutableList(2, 2341, 3, 784)

使用HashSets的解决方案肯定是最快的,正如他已经指出的使用.size并没有产生重大影响.

Ale*_*nov 15

这是我能想到的最快的纯功能解决方案:

def isUniqueList(l: List[T]) = isUniqueList1(l, new HashSet[T])

@tailrec
def isUniqueList1(l: List[T], s: Set[T]) = l match {
  case Nil => true
  case (h :: t) => if (s(h)) false else isUniqueList1(t, s + h)
}
Run Code Online (Sandbox Code Playgroud)

这应该更快,但使用可变数据结构(基于distinctVasil Remeniuk给出的实现):

def isUniqueList(l: List[T]): Boolean = {
  val seen = mutable.HashSet[A]()
  for (x <- this) {
    if (seen(x)) {
      return false
    }
    else {
      seen += x
    }
  }
  true
}
Run Code Online (Sandbox Code Playgroud)

这是最简单的(相当于你的):

def isUniqueList(l: List[T]) = l.toSet.size == l.size
Run Code Online (Sandbox Code Playgroud)

  • 但是它不再是尾递归...我至少应用de Morgan的规则并把它变成`!s(h)&& isUniqueList(t,s + h)`,这是. (3认同)
  • 你可以改变`if(s.contains(h))false else isUniqueList1(t,s + h)`to:`!(s(h)||!isUniqueList(t,s + h))` (2认同)

Vas*_*iuk 6

我只想使用distinct方法:

scala> val l = List(1,2,3,4,3)
l: List[Int] = List(1, 2, 3, 4, 3)

scala> l.distinct.size == l.size
res2: Boolean = false
Run Code Online (Sandbox Code Playgroud)


ADD:标准distinct实现(from scala.collection.SeqLike)使用可变HashSet来查找重复元素:

  def distinct: Repr = {
    val b = newBuilder
    val seen = mutable.HashSet[A]()
    for (x <- this) {
      if (!seen(x)) {
        b += x
        seen += x
      }
    }
    b.result
  }
Run Code Online (Sandbox Code Playgroud)

  • 不,因为`List.size`本身就是O(n).请参阅http://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_8_0_final/src//library/scala/collection/immutable/List.scala#L1以检查是否必须通过迭代计算大小列表. (2认同)