对比 obj = = nil

使用 obj.nil?obj == nil是否更好? 两者的好处是什么?

19052 次浏览

就个人而言,我更喜欢 object.nil?,因为它在较长的代码行上不那么令人困惑; 但是,如果我在 Rails 中工作,我通常也会使用 object.blank?,因为它也会检查变量是否为空。

我发现自己根本不使用 .nil?当你可以做:

unless obj
// do work
end

实际上使用 .nil?会慢一些,但不会明显。.nil?只是一个检查对象是否等于零的方法,除了视觉上的吸引力和它所需要的很少的性能没有什么区别。

一些 可能建议使用. nil? 比简单的比较慢,这在您考虑时是有意义的。

但是,如果您不关心规模和速度,那么.nil? 可能更具可读性。

使用 objec.nil 或 obj = = nil 更好吗

它具有从外部观察到的完全相同的效果(pfff) < sup > *

这两者的好处是什么。

如果您喜欢微优化 所有,那么除了对象 nil本身之外,对象将返回 false.nil?消息,而使用 ==消息的对象将执行一个微小的微比较 与另一个对象确定它是否是同一个对象。

* 查看评论。

您可以在 nil?上使用符号 # to _ proc,而在 x == nil上则不实用。

arr = [1, 2, 3]
arr.any?(&:nil?) # Can be done
arr.any?{|x| x == nil} # More verbose, and I suspect is slower on Ruby 1.9.2
! arr.all? # Check if any values are nil or false

在许多情况下,都不是,只测试布尔真值

虽然这两个操作有很大的不同,但我确信它们总是会产生相同的结果,至少在某些边缘的人决定覆盖 Object 的 #nil?方法之前是如此。(一个调用从 Object 继承的 #nil?方法或在 NilClass中重写的方法,另一个调用与 nil单例进行比较。)

我建议,当你有疑问的时候,你可以选择第三种方式,实际上,只是测试一个表达式的真值。

因此,if x而不是 if x == nilif x.nil?,以便在表达式值为 假的时进行此测试 DTRT。以这种方式工作也可能有助于避免诱惑别人将 FalseClass#nil?定义为 没错

撇开语法和样式不谈,我想看看不同的 nil 测试方法是如何“相同”的。因此,我编写了一些基准来查看,并对其进行了各种形式的 null 测试。

首先是 DR 结果

实际结果表明,使用 obj作为空检查在所有情况下都是最快的。obj始终比检查 obj.nil?快30% 或更多。

令人惊讶的是,obj的执行速度大约是 obj == nil的3-4倍,对此似乎有一个惩罚性的性能损失。

想要将性能密集型算法的速度提高200% -300% 吗?将所有 obj == nil检查转换为 obj。想要阻碍代码的性能吗?尽可能使用 obj == nil。(只是开个玩笑: 不要把你的代码放在沙袋里!).

归根结底,始终使用 obj。这与 红宝石风格指南规则: 除非处理的是布尔值,否则不要执行显式的非零检查。是一致的

基准条件

好了,结果出来了。那么,这个基准是如何组合在一起的,完成了哪些测试,结果的细节是什么?

我想到的空头支票是:

  • obj
  • obj.nil?
  • !obj
  • !!obj
  • obj == nil
  • obj != nil

我选择了各种 Ruby 类型进行测试,以防结果根据类型发生变化。这些类型是 FixnumFloatFalseClassTrueClassStringRegex

我对每种类型都使用了这些 nil 检查条件,以查看它们之间在性能方面是否存在差异。对于每种类型,我都测试了 nil 对象和非 nil 值对象(例如 1_000_000100_000.0falsetrue"string"/\w/) ,看看在一个为 nil 的对象上检查 nil 与在一个不为 nil 的对象上检查 nil 是否有区别。

基准

所有这些问题都解决了,下面是基准代码:

require 'benchmark'


nil_obj = nil
N = 10_000_000


puts RUBY_DESCRIPTION


[1_000_000, 100_000.0, false, true, "string", /\w/].each do |obj|
title = "#{obj} (#{obj.class.name})"
puts "============================================================"
puts "Running tests for obj = #{title}"


Benchmark.bm(15, title) do |x|
implicit_obj_report   = x.report("obj:")            { N.times { obj            } }
implicit_nil_report   = x.report("nil_obj:")        { N.times { nil_obj        } }
explicit_obj_report   = x.report("obj.nil?:")       { N.times { obj.nil?       } }
explicit_nil_report   = x.report("nil_obj.nil?:")   { N.times { nil_obj.nil?   } }
not_obj_report        = x.report("!obj:")           { N.times { !obj           } }
not_nil_report        = x.report("!nil_obj:")       { N.times { !nil_obj       } }
not_not_obj_report    = x.report("!!obj:")          { N.times { !!obj          } }
not_not_nil_report    = x.report("!!nil_obj:")      { N.times { !!nil_obj      } }
equals_obj_report     = x.report("obj == nil:")     { N.times { obj == nil     } }
equals_nil_report     = x.report("nil_obj == nil:") { N.times { nil_obj == nil } }
not_equals_obj_report = x.report("obj != nil:")     { N.times { obj != nil     } }
not_equals_nil_report = x.report("nil_obj != nil:") { N.times { nil_obj != nil } }
end
end

结果

结果很有趣,因为 Fixnum、 Float 和 String 类型的性能几乎相同,Regex 几乎相同,而 FalseClass 和 TrueClass 的性能要快得多。测试是在 MRI 版本1.9.3、2.0.0、2.1.5和2.2.5上进行的,各个版本的比较结果非常相似。MRI 2.2.5版本的结果如下所示(可在 大意中获得:

ruby 2.2.5p319 (2016-04-26 revision 54774) [x86_64-darwin14]
============================================================
Running tests for obj = 1000000 (Fixnum)
user     system      total        real
obj:              0.970000   0.000000   0.970000 (  0.987204)
nil_obj:          0.980000   0.010000   0.990000 (  0.980796)
obj.nil?:         1.250000   0.000000   1.250000 (  1.268564)
nil_obj.nil?:     1.280000   0.000000   1.280000 (  1.287800)
!obj:             1.050000   0.000000   1.050000 (  1.064061)
!nil_obj:         1.070000   0.000000   1.070000 (  1.170393)
!!obj:            1.110000   0.000000   1.110000 (  1.122204)
!!nil_obj:        1.120000   0.000000   1.120000 (  1.147679)
obj == nil:       2.110000   0.000000   2.110000 (  2.137807)
nil_obj == nil:   1.150000   0.000000   1.150000 (  1.158301)
obj != nil:       2.980000   0.010000   2.990000 (  3.041131)
nil_obj != nil:   1.170000   0.000000   1.170000 (  1.203015)
============================================================
Running tests for obj = 100000.0 (Float)
user     system      total        real
obj:              0.940000   0.000000   0.940000 (  0.947136)
nil_obj:          0.950000   0.000000   0.950000 (  0.986488)
obj.nil?:         1.260000   0.000000   1.260000 (  1.264953)
nil_obj.nil?:     1.280000   0.000000   1.280000 (  1.306817)
!obj:             1.050000   0.000000   1.050000 (  1.058924)
!nil_obj:         1.070000   0.000000   1.070000 (  1.096747)
!!obj:            1.100000   0.000000   1.100000 (  1.105708)
!!nil_obj:        1.120000   0.010000   1.130000 (  1.132248)
obj == nil:       2.140000   0.000000   2.140000 (  2.159595)
nil_obj == nil:   1.130000   0.000000   1.130000 (  1.151257)
obj != nil:       3.010000   0.000000   3.010000 (  3.042263)
nil_obj != nil:   1.170000   0.000000   1.170000 (  1.189145)
============================================================
Running tests for obj = false (FalseClass)
user     system      total        real
obj:              0.930000   0.000000   0.930000 (  0.933712)
nil_obj:          0.950000   0.000000   0.950000 (  0.973776)
obj.nil?:         1.250000   0.000000   1.250000 (  1.340943)
nil_obj.nil?:     1.270000   0.010000   1.280000 (  1.282267)
!obj:             1.030000   0.000000   1.030000 (  1.039532)
!nil_obj:         1.060000   0.000000   1.060000 (  1.068765)
!!obj:            1.100000   0.000000   1.100000 (  1.111930)
!!nil_obj:        1.110000   0.000000   1.110000 (  1.115355)
obj == nil:       1.110000   0.000000   1.110000 (  1.121403)
nil_obj == nil:   1.100000   0.000000   1.100000 (  1.114550)
obj != nil:       1.190000   0.000000   1.190000 (  1.207389)
nil_obj != nil:   1.140000   0.000000   1.140000 (  1.181232)
============================================================
Running tests for obj = true (TrueClass)
user     system      total        real
obj:              0.960000   0.000000   0.960000 (  0.964583)
nil_obj:          0.970000   0.000000   0.970000 (  0.977366)
obj.nil?:         1.260000   0.000000   1.260000 (  1.265229)
nil_obj.nil?:     1.270000   0.010000   1.280000 (  1.283342)
!obj:             1.040000   0.000000   1.040000 (  1.059689)
!nil_obj:         1.070000   0.000000   1.070000 (  1.068290)
!!obj:            1.120000   0.000000   1.120000 (  1.154803)
!!nil_obj:        1.130000   0.000000   1.130000 (  1.155932)
obj == nil:       1.100000   0.000000   1.100000 (  1.102394)
nil_obj == nil:   1.130000   0.000000   1.130000 (  1.160324)
obj != nil:       1.190000   0.000000   1.190000 (  1.202544)
nil_obj != nil:   1.200000   0.000000   1.200000 (  1.200812)
============================================================
Running tests for obj = string (String)
user     system      total        real
obj:              0.940000   0.000000   0.940000 (  0.953357)
nil_obj:          0.960000   0.000000   0.960000 (  0.962029)
obj.nil?:         1.290000   0.010000   1.300000 (  1.306233)
nil_obj.nil?:     1.240000   0.000000   1.240000 (  1.243312)
!obj:             1.030000   0.000000   1.030000 (  1.046630)
!nil_obj:         1.060000   0.000000   1.060000 (  1.123925)
!!obj:            1.130000   0.000000   1.130000 (  1.144168)
!!nil_obj:        1.130000   0.000000   1.130000 (  1.147330)
obj == nil:       2.320000   0.000000   2.320000 (  2.341705)
nil_obj == nil:   1.100000   0.000000   1.100000 (  1.118905)
obj != nil:       3.040000   0.010000   3.050000 (  3.057040)
nil_obj != nil:   1.150000   0.000000   1.150000 (  1.162085)
============================================================
Running tests for obj = (?-mix:\w) (Regexp)
user     system      total        real
obj:              0.930000   0.000000   0.930000 (  0.939815)
nil_obj:          0.960000   0.000000   0.960000 (  0.961852)
obj.nil?:         1.270000   0.000000   1.270000 (  1.284321)
nil_obj.nil?:     1.260000   0.000000   1.260000 (  1.275042)
!obj:             1.040000   0.000000   1.040000 (  1.042543)
!nil_obj:         1.040000   0.000000   1.040000 (  1.047280)
!!obj:            1.120000   0.000000   1.120000 (  1.128137)
!!nil_obj:        1.130000   0.000000   1.130000 (  1.138988)
obj == nil:       1.520000   0.010000   1.530000 (  1.529547)
nil_obj == nil:   1.110000   0.000000   1.110000 (  1.125693)
obj != nil:       2.210000   0.000000   2.210000 (  2.226783)
nil_obj != nil:   1.170000   0.000000   1.170000 (  1.169347)