有序集合的目的是什么?
What is the purpose of sorted sets?
Clojure 有一个函数 sorted-set
which creates a PersistentTreeSet
object. As the name implies, sorted-set
创建唯一对象的排序集合。
排序集什么时候有用?什么时候用sorted-set
than sort
and distinct
比较好?
=> (apply sorted-set [2 2 1 1 3 3])
#{1 2 3}
=> (sort (distinct [2 2 1 1 3 3]))
(1 2 3)
就我个人而言,如果我希望在添加元素时没有重复的有序数据结构,我会使用排序集。也就是说,我从一个空集开始,而不是将其应用于列表。
我会使用 sort 和 distinct 的时间是如果我有任何其他数据结构,比如我想要排序并删除重复项的列表。
基本上,应用集合会为您提供一个具有独特元素的新对象,而不同的对象作用于相同的列表引用。
排序集与调用 sort
和 distinct
的结果之间的区别在于结果类型是一个集。
这为您提供 O(log N) 性能(想想二进制搜索)来检查元素是否在集合中 (contains?
) 或添加一个元素 (conj
),而在列表,由 sort
和 distinct
返回,默认情况下您将获得更差的特性来实现相同的行为。
排序集在您需要集合语义时很有用——快速 contains?
、conj
和 disj
(= 元素删除),如 Leon 所解释的那样——以及井中的遍历定义的顺序。在内置排序集(和映射)的情况下,可以对整个集(seq
、rseq
)和任何 "subrange"(subseq
、 rsubseq
) 在两个键之间,包括或不包括。
如果您愿意使用非核心集合,Contrib 库 data.avl(我是它的作者和维护者)提供了一种带有附加功能的排序集和映射 – nth
用于按等级访问集合元素,rank-of
用于发现集合中元素的等级,最近邻查询,以及 "subrange" 和类似拆分的操作 return输入集合的完全功能子集(想想 subseq
returning 一个完全功能的 子集 原始的,而不仅仅是一个 seq,而不保留任何元素出于 GC 的目的,原始不存在于子集中)。所有这些在最坏情况下都在 O(log n) 时间内运行,就像标准的排序集操作一样。
如果您只需要 contains?
+ conj
+ disj
,您可能会想改用散列集,因为它们往往会为这些操作提供更好的性能。然而,值得注意的是,如果您预期将来自可能恶意的外部源的输入添加到您的集合中,那么即使您不关心顺序,您也可能希望使用排序集合。这是因为哈希集的性能在存在哈希冲突的情况下会降低到 O(n)(对手可能会强制执行,使用的哈希函数是确定性的并且预先固定),而排序集的 O(log n) 是硬保证。
如果您只需要对输入集合进行一次排序,然后遍历整个集合,或者重复遍历其中的多个 prefixes/suffixes,那么构建一个由唯一项组成的排序向量可能确实是更好的选择。即使对于仅遍历的工作负载,排序集仍然可能更可取,但是,如果您需要从集合的任意元素开始的 subseq
/rsubseq
特性((subseq a-set >= 5)
= seq a-set
的那些元素在 a-set
的排序方面 >= 5)。
Clojure 有一个函数 sorted-set
which creates a PersistentTreeSet
object. As the name implies, sorted-set
创建唯一对象的排序集合。
排序集什么时候有用?什么时候用sorted-set
than sort
and distinct
比较好?
=> (apply sorted-set [2 2 1 1 3 3])
#{1 2 3}
=> (sort (distinct [2 2 1 1 3 3]))
(1 2 3)
就我个人而言,如果我希望在添加元素时没有重复的有序数据结构,我会使用排序集。也就是说,我从一个空集开始,而不是将其应用于列表。
我会使用 sort 和 distinct 的时间是如果我有任何其他数据结构,比如我想要排序并删除重复项的列表。
基本上,应用集合会为您提供一个具有独特元素的新对象,而不同的对象作用于相同的列表引用。
排序集与调用 sort
和 distinct
的结果之间的区别在于结果类型是一个集。
这为您提供 O(log N) 性能(想想二进制搜索)来检查元素是否在集合中 (contains?
) 或添加一个元素 (conj
),而在列表,由 sort
和 distinct
返回,默认情况下您将获得更差的特性来实现相同的行为。
排序集在您需要集合语义时很有用——快速 contains?
、conj
和 disj
(= 元素删除),如 Leon 所解释的那样——以及井中的遍历定义的顺序。在内置排序集(和映射)的情况下,可以对整个集(seq
、rseq
)和任何 "subrange"(subseq
、 rsubseq
) 在两个键之间,包括或不包括。
如果您愿意使用非核心集合,Contrib 库 data.avl(我是它的作者和维护者)提供了一种带有附加功能的排序集和映射 – nth
用于按等级访问集合元素,rank-of
用于发现集合中元素的等级,最近邻查询,以及 "subrange" 和类似拆分的操作 return输入集合的完全功能子集(想想 subseq
returning 一个完全功能的 子集 原始的,而不仅仅是一个 seq,而不保留任何元素出于 GC 的目的,原始不存在于子集中)。所有这些在最坏情况下都在 O(log n) 时间内运行,就像标准的排序集操作一样。
如果您只需要 contains?
+ conj
+ disj
,您可能会想改用散列集,因为它们往往会为这些操作提供更好的性能。然而,值得注意的是,如果您预期将来自可能恶意的外部源的输入添加到您的集合中,那么即使您不关心顺序,您也可能希望使用排序集合。这是因为哈希集的性能在存在哈希冲突的情况下会降低到 O(n)(对手可能会强制执行,使用的哈希函数是确定性的并且预先固定),而排序集的 O(log n) 是硬保证。
如果您只需要对输入集合进行一次排序,然后遍历整个集合,或者重复遍历其中的多个 prefixes/suffixes,那么构建一个由唯一项组成的排序向量可能确实是更好的选择。即使对于仅遍历的工作负载,排序集仍然可能更可取,但是,如果您需要从集合的任意元素开始的 subseq
/rsubseq
特性((subseq a-set >= 5)
= seq a-set
的那些元素在 a-set
的排序方面 >= 5)。