Scala:我可以依赖集合中项目的顺序吗?
这真是一个令人不快的惊喜:Scala:我可以依赖集合中项目的顺序吗?,scala,set,Scala,Set,这真是一个令人不快的惊喜: 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.col
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
方法。问题是,我想在接口级别强制项目的唯一性(同时保留顺序),因此使用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
我相信,你永远不应该依赖于一组中的顺序。没有语言
除此之外,还可以看一看哪些方面会深入讨论这个问题。这取决于您使用的设备。如果您不知道您有哪一套实现,那么答案很简单:不,您不能确定。在实践中,我通常会遇到以下三种情况:
我需要订购集合中的物品。为此,我在SortedSet
特性中使用类混合,当您仅使用标准Scala API时,它始终是TreeSet
。它保证根据元素的compareTo
方法对元素进行排序(请参见ordered
trat)。排序会带来(非常)小的性能损失,因为插入/检索的运行时现在是对数的,而不像HashSet
那样(几乎)是常数(假设有一个好的哈希函数)
您需要保留项目插入的顺序。然后使用LinkedHashSet
。实际上,与普通的哈希集一样快,元素之间的额外链接需要多一点存储空间
您不关心集合中的顺序。因此,您使用了一个哈希集
。(这是使用集合时的默认值。如第一个示例中所示,应用方法)
所有这些也适用于Java,Java有一个TreeSet
,LinkedHashSet
和HashSet
以及相应的接口SortedSet
,Comparable
和plainSetListSet
将始终以插入的相反顺序返回元素,因为它由列表支持,而将元素添加到列表中的最佳方法是预先添加元素
如果您想要先入先出(队列),则不可变的数据结构是有问题的。您可以获得O(logn)
或摊销O(1)
。考虑到显然需要构建集合,然后从集合中生成迭代器(即,首先放置所有元素,然后移除所有元素),我看不到任何方法来摊销它
您可以相信,ListSet
总是以后进先出的顺序(堆栈)返回元素。如果这就足够了,那就开始吧。我在哪里可以比“scala编程”和Scaladocs更深入地了解scala的集合?这是一个开始学习scala集合的好地方:集合的(数学)定义没有顺序,大多数语言都遵循这一惯例。好奇的是,你为什么希望他们会有这样的想法?除了唯一性,顺序是Seq
和Set
之间唯一的实质性区别!在这种情况下,我不太关心数据结构的数学特性。我只需要一个不可变的不同项目集合,保留它们的原始顺序。称之为列表、集合、向量、表格或“卡车”:)谢谢,我没有找到其他问题。。我的BADIN C++, STD::SET保证被排序。1.@larsmans…举个例子?一个例子没有提供OP想要的顺序(即插入顺序)?真的吗?同意@larsmans的说法。在C++中,STD::SET是命令,而STD::unOrdEdStEy不是命令Chrestoh Henkelmann的答案是正确答案。唯一性和分类性是正交属性,在集合类中(在任何语言中)两者都是完全合法的。如果你看一下,你会发现它所做的只是混合基本的集合特征,并添加一个存储元素顺序的列表成员。因此,按照这种模式,您可以简单地使用不可变包中相应的基本特征和类编写您自己的不可变LinkedHashSet。插入顺序不是先验的明确定义:如果相同元素添加两次,会发生什么情况,即一个表示符将放在何处,依赖于给定类型的实现不是一个好主意。您需要通过其接口定义(契约)确保维持秩序的东西。我想不出一个围绕现有不可变集合的简单包装器可以做到这一点。您可以使用TreeMap[(Int,T)]
(跟踪第一个组件中的插入顺序),但这意味着显式地委托所有集合方法。Scala API文档(遗憾地)没有提到它,但根据代码,再次插入元素不会改变其位置(这也是JavaLinkedHashMap
的工作方式):if(addEntry(elem)){ordered+=elem;true}else false
。Java API更精确:“注意,如果密钥重新插入映射,插入顺序不受影响”有时,依赖于给定类型的实现是很重要的。当我需要一个具有固定时间前缀的列表时,我需要使用链表,而不是数组支持的列表。这就是为什么在这种情况下,我显式地使用实现的类型,而不是interf