Set
的主要优势似乎是保持独特的元素。但这可以很容易地在Array
实现,
array = [2,3,4]
array | [2,5,6] # => [2,3,4,5,6]
我遇到的唯一不同的功能(可能适用于少数用例)是,
set1 = [1,2,3].to_set
set2 = [2,1,3].to_set
set1 == set2 # => true
[1,2,3] == [2,1,3] # => false
由于Array
具有与之相关的各种功能和操作,我何时以及为什么要使用Set
?
有许多链接比较Array
和Set
但我没有遇到Set
的重要应用。
当然,无论你用Set
做什么,都有办法用Array
做到这一点。使用Set
的优点在于,由于它是基于Hash
实现的,因此大多数操作都是O(1)复杂度,而使用Array
执行它可能是O(n)。
例如:
Set.new([1, 2, 3]).include?(2) # O(1) complexity
[1, 2, 3].include?(2) # O(n) complexity
这两个类定义了不同的数据结构:
集合实际上取自数学概念:https://en.wikipedia.org/wiki/Set_(mathematics)
在Ruby内部设置使用哈希存储,如文档中所述:
Set使用Hash作为存储,因此您必须注意以下几点:
元素的相等性是根据Object#eql确定的?和Object #hash。 Set假定每个元素的标识在存储时不会更改。修改集合的元素会将集合呈现为不可靠状态。当要存储字符串时,将存储字符串的冻结副本,除非原始字符串已被冻结。
当您查看代码时,它在内部存储为哈希,用户给定的对象作为键,布尔值作为值(确切地说:添加对象时为true)。
为什么要使用套装?如果您想要强制执行唯一性而且您不需要任何订购 - 套装是您的最佳选择。当您不关心唯一性并且订购很重要时 - Array是您的选择。
否则 - 你需要仲裁决定;)
由于显而易见的原因,请在此处查看其他答案。出于性能原因,请参阅MRI Ruby 1.9.3中此小基准测试的结果:
require 'benchmark'
require 'set'
array = (1..100000).to_a
set = array.to_set
#hash = Hash[array.map {|x| [x, nil]}] #beter voor heel grote volumes mar trager
hash = Hash[*array]
Benchmark.bmbm do |x|
x.report("Set.include?") { 10000.times { set.include?(99999) } }
x.report("Array.include?") { 10000.times { array.include?(99999) } }
x.report("Hash.include?") { 10000.times { hash.include?(99999) } }
end
这使
Rehearsal --------------------------------------------------
Set.include? 0.000000 0.000000 0.000000 ( 0.015604)
Array.include? 37.940000 0.000000 37.940000 ( 38.651992)
Hash.include? 0.000000 0.000000 0.000000 ( 0.001000)
---------------------------------------- total: 37.940000sec
user system total real
Set.include? 0.000000 0.000000 0.000000 ( 0.002001)
Array.include? 38.157000 0.000000 38.157000 ( 38.730615)
Hash.include? 0.000000 0.000000 0.000000 ( 0.001001)
足够的理由尽可能使用Set
或Hash
。
好吧,从技术上讲,你可以使用Ruby Arrays
和Sets
获得相同的结果。但是,关于Ruby的Modules
和Classes
也是如此。你可以在两者中都有变量和方法,但是它们可以用于不同的目的,当别人读取代码时,他/她将获得这种意义,而不会试图弄清楚你的设计决策。
我想Sets
和Arrays
的情况是一样的。您可以使用数组实现相同的结果,但是使用集合,您的变量可以传达有关业务逻辑的其他信息(内容必须是唯一的)。基本上,它们是不同的数据结构。我们有不同的数据结构是有原因的。
其次,使用套装时,你的代码更具可读性,当你进行像.subset?
,.superset?
,.intersect?
等操作时。新来者会猜测array | array
操作的作用,但是套装就是那里。看看哪一个更具可读性:
([1, 2, 3] & [2, 3]).empty? # => false
Set[1, 2, 3].intersect? Set[2, 3] # => true
第三,与集合的交集操作可能是预先优化的并且可以更快地运行。