【发布时间】:2011-08-21 00:41:20
【问题描述】:
假设我有
val dirty = List("a", "b", "a", "c")
是否有返回“a”、“b”、“c”的列表操作
【问题讨论】:
标签: scala
假设我有
val dirty = List("a", "b", "a", "c")
是否有返回“a”、“b”、“c”的列表操作
【问题讨论】:
标签: scala
查看 Seq 的 ScalaDoc,
scala> dirty.distinct
res0: List[java.lang.String] = List(a, b, c)
更新。其他人建议使用Set 而不是List。这很好,但请注意,默认情况下,Set 接口不保留元素顺序。您可能希望使用明确确实保留顺序的 Set 实现,例如 collection.mutable.LinkedHashSet。
scala.collection.immutable.List 现在有一个.distinct 方法。
所以现在可以调用dirty.distinct,而无需转换为Set 或Seq。
【讨论】:
.distinct 没有为scala.collection.Iterable[A] 定义。所以在这种情况下,你必须使用升级dirty 到Seq 或Set 无论如何(即通过使用.toList、.toSeq 或.toSet 成员)才能工作。
在使用 Kitpon 的解决方案之前,请考虑使用 Set 而不是 List,它可以确保每个元素都是唯一的。
由于大多数列表操作(foreach、map、filter、...)对于集合和列表都是相同的,因此在代码中更改集合非常容易。
【讨论】:
当然,首先使用 Set 是正确的方法,但是:
scala> List("a", "b", "a", "c").toSet.toList
res1: List[java.lang.String] = List(a, b, c)
有效。或者只是 toSet,因为它支持 Seq Traversable 接口。
【讨论】:
Set 实现了Traversable,而不是Seq。区别在于Seq 保证元素的顺序,而Traversable 不保证。
如果你碰巧想要一个列表中你知道已经排序的不同项目,正如我经常需要的那样,下面的执行速度大约是.distinct 的两倍:
def distinctOnSorted[V](seq: List[V]): List[V] =
seq.foldLeft(List[V]())((result, v) =>
if (result.isEmpty || v != result.head) v :: result else result)
.reverse
在 0-99 的 100,000,000 个随机 Int 列表上的性能结果:
distinct : 0.6655373s
distinctOnSorted: 0.2848134s
虽然看起来更可变/非函数式编程方法可能比预先添加到不可变列表更快,但实践表明并非如此。不可变的实现始终表现得更好。我的猜测是 scala 将其编译器优化集中在不可变集合上,并且做得很好。 (欢迎其他人提交更好的实现。)
List size 1e7, random 0 to 1e6
------------------------------
distinct : 4562.2277ms
distinctOnSorted : 201.9462ms
distinctOnSortedMut1: 4399.7055ms
distinctOnSortedMut2: 246.099ms
distinctOnSortedMut3: 344.0758ms
distinctOnSortedMut4: 247.0685ms
List size 1e7, random 0 to 100
------------------------------
distinct : 88.9158ms
distinctOnSorted : 41.0373ms
distinctOnSortedMut1: 3283.8945ms
distinctOnSortedMut2: 54.4496ms
distinctOnSortedMut3: 58.6073ms
distinctOnSortedMut4: 51.4153ms
实现:
object ListUtil {
def distinctOnSorted[V](seq: List[V]): List[V] =
seq.foldLeft(List[V]())((result, v) =>
if (result.isEmpty || v != result.head) v :: result else result)
.reverse
def distinctOnSortedMut1[V](seq: List[V]): Seq[V] = {
if (seq.isEmpty) Nil
else {
val result = mutable.MutableList[V](seq.head)
seq.zip(seq.tail).foreach { case (prev, next) =>
if (prev != next) result += next
}
result //.toList
}
}
def distinctOnSortedMut2[V](seq: List[V]): Seq[V] = {
val result = mutable.MutableList[V]()
if (seq.isEmpty) return Nil
result += seq.head
var prev = seq.head
for (v <- seq.tail) {
if (v != prev) result += v
prev = v
}
result //.toList
}
def distinctOnSortedMut3[V](seq: List[V]): List[V] = {
val result = mutable.MutableList[V]()
if (seq.isEmpty) return Nil
result += seq.head
var prev = seq.head
for (v <- seq.tail) {
if (v != prev) v +=: result
prev = v
}
result.reverse.toList
}
def distinctOnSortedMut4[V](seq: List[V]): Seq[V] = {
val result = ListBuffer[V]()
if (seq.isEmpty) return Nil
result += seq.head
var prev = seq.head
for (v <- seq.tail) {
if (v != prev) result += v
prev = v
}
result //.toList
}
}
测试:
import scala.util.Random
class ListUtilTest extends UnitSpec {
"distinctOnSorted" should "return only the distinct elements in a sorted list" in {
val bigList = List.fill(1e7.toInt)(Random.nextInt(100)).sorted
val t1 = System.nanoTime()
val expected = bigList.distinct
val t2 = System.nanoTime()
val actual = ListUtil.distinctOnSorted[Int](bigList)
val t3 = System.nanoTime()
val actual2 = ListUtil.distinctOnSortedMut1(bigList)
val t4 = System.nanoTime()
val actual3 = ListUtil.distinctOnSortedMut2(bigList)
val t5 = System.nanoTime()
val actual4 = ListUtil.distinctOnSortedMut3(bigList)
val t6 = System.nanoTime()
val actual5 = ListUtil.distinctOnSortedMut4(bigList)
val t7 = System.nanoTime()
actual should be (expected)
actual2 should be (expected)
actual3 should be (expected)
actual4 should be (expected)
actual5 should be (expected)
val distinctDur = t2 - t1
val ourDur = t3 - t2
ourDur should be < (distinctDur)
print(s"distinct : ${distinctDur / 1e6}ms\n")
print(s"distinctOnSorted : ${ourDur / 1e6}ms\n")
print(s"distinctOnSortedMut1: ${(t4 - t3) / 1e6}ms\n")
print(s"distinctOnSortedMut2: ${(t5 - t4) / 1e6}ms\n")
print(s"distinctOnSortedMut3: ${(t6 - t5) / 1e6}ms\n")
print(s"distinctOnSortedMut4: ${(t7 - t6) / 1e6}ms\n")
}
}
【讨论】:
您还可以使用递归和模式匹配:
def removeDuplicates[T](xs: List[T]): List[T] = xs match {
case Nil => xs
case head :: tail => head :: removeDuplicates(for (x <- tail if x != head) yield x)
}
【讨论】:
removeDuplicates(tail.filter(_ != head))
inArr.distinct foreach println _
【讨论】:
算法方式...
def dedupe(str: String): String = {
val words = { str split " " }.toList
val unique = words.foldLeft[List[String]] (Nil) {
(l, s) => {
val test = l find { _.toLowerCase == s.toLowerCase }
if (test == None) s :: l else l
}
}.reverse
unique mkString " "
}
【讨论】: