这是一个非常不可思议的惊喜:
scala> Set(1, 2, 3, 4, 5)
res18: scala.collection.immutable.Set[Int] = Set(4, 5, 1, 2, 3)
scala> Set(1, 2, 3, 4, 5).toList
res25: List[Int] = List(5, 1, 2, 3, 4)
这个例子本身就我的问题提出了“不”答案。那么ListSet
呢?
scala> import scala.collection.immutable.ListSet
scala> ListSet(1, 2, 3, 4, 5)
res21: scala.collection.immutable.ListSet[Int] = Set(1, 2, 3, 4, 5)
这个似乎有效,但我应该依赖这种行为吗? 还有哪些其他数据结构适用于必须保留原始订单的唯一项目的不可变集合?
顺便说一句,我确实知道distict
中的List
方法。问题是,我想在接口级别强制执行项目的唯一性(同时保留顺序),因此使用distinct
会弄乱我的整洁设计..
修改
ListSet
似乎也不太可靠:
scala> ListSet(1, 2, 3, 4, 5).toList
res28: List[Int] = List(5, 4, 3, 2, 1)
EDIT2
在寻找完美设计时,我试过这个:
scala> class MyList[A](list: List[A]) { val values = list.distinct }
scala> implicit def toMyList[A](l: List[A]) = new MyList(l)
scala> implicit def fromMyList[A](l: MyList[A]) = l.values
实际上有效:
scala> val l1: MyList[Int] = List(1, 2, 3)
scala> l1.values
res0: List[Int] = List(1, 2, 3)
scala> val l2: List[Int] = new MyList(List(1, 2, 3))
l2: List[Int] = List(1, 2, 3)
然而,问题是我不想在库外公开MyList
。在覆盖时有没有办法进行隐式转换?例如:
trait T { def l: MyList[_] }
object O extends T { val l: MyList[_] = List(1, 2, 3) }
scala> O.l mkString(" ") // Let's test the implicit conversion
res7: String = 1 2 3
我想这样做:
object O extends T { val l = List(1, 2, 3) } // Doesn't work
答案 0 :(得分:43)
这取决于你正在使用的套装。如果您不知道您拥有哪个Set实现,那么答案很简单,不,您不能确定。在实践中,我经常遇到以下三种情况:
我需要订购套装中的物品。为此,我使用SortedSet
特征混合的类,当您仅使用标准Scala API时,它始终是TreeSet
。它保证元素按照compareTo
方法排序(参见Ordered
trat)。由于插入/检索的运行时现在是对数的,因此得到(非常)小的性能损失,而不是(几乎)常量,就像HashSet
一样(假设一个好的散列函数)。
您需要保留项目的插入顺序。然后使用LinkedHashSet
。实际上与正常HashSet
一样快,需要更多的存储空间来存储元素之间的其他链接。
您不关心Set中的顺序。所以你使用HashSet
。 (这是使用第一个示例中的Set.apply
方法时的默认设置)
这一切也适用于Java,Java有TreeSet
,LinkedHashSet
和HashSet
以及相应的接口SortedSet
,Comparable
和普通{{ 1}}。
答案 1 :(得分:12)
我相信你永远不应该依赖于集合中的顺序。没有语言。
除此之外,请查看this question深入探讨这一点。
答案 2 :(得分:11)
ListSet
将始终按照与插入相反的顺序返回元素,因为它由List
支持,并且向List
添加元素的最佳方式是预先添加它们。 / p>
如果您想要先进先出(队列),则不可变数据结构会出现问题。您可以获得O(logn)
或摊销O(1)
。鉴于显然需要构建集合然后从中生成迭代器(即,您将首先放置所有元素,然后您将删除所有元素),我没有看到任何方法来分摊它。
你可以依赖ListSet
总是返回后进先出顺序(堆栈)的元素。如果那就够了,那就去吧。