为什么从基于大数组的大散列中选择值如此之慢?

时间:2013-04-02 18:13:51

标签: ruby

我有一个大约150k元素的哈希,以及一个25k元素的数组。我需要创建一个新的哈希,或修改现有的哈希,以删除其键不在数组上的所有元素。这就是我现在所拥有的:

hash.select {|k,v| array.include?(k)}
new_hash = hash.delete_if {|k,v| !array.include?(k)}

由于比较复杂,这两种方法非常慢。有没有办法可以加速?

2 个答案:

答案 0 :(得分:4)

(hash.keys - array).each{|k| hash.delete(k)}

或者,这可能更快:

keys_to_be_removed = {}
hash.each{|k, _| keys_to_be_removed[k] = true}
array.each{|k| keys_to_be_removed[k] = false}
keys_to_be_removed.each{|k, v| hash.delete(k) if v}

重点是避免数组操作,尽可能多地使用哈希值。

答案 1 :(得分:2)

@ sawa的回答很好,速度很快,但它确实让你表达了比Hash#select更尴尬的意图。如果数组是带有O(1)查找的Set而不是带有O(N)查找的数组,那么您的初始方法就可以正常工作。

require 'set'

set = array.to_set
hash.select { |k,v| set.include?(k) }

This microbenchmark演示集很快,并且这种方法比预先构建集合时推荐的密钥减法删除方法@sawa略快,如果必须在运行中设置稍微慢一点。

                          user     system      total        real
noop                    0.600000   0.020000   0.620000 (  0.614905)
keys_minus_arr_delete   1.190000   0.020000   1.210000 (  1.213376)
select_set_include      1.050000   0.010000   1.060000 (  1.084079)
select_set_include_fly  1.350000   0.020000   1.370000 (  1.361623)
sawa2                   1.860000   0.020000   1.880000 (  1.870162)