obj.nil? vs. obj == nil

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

使用obj.nil更好吗? 或者obj == nil

这完全一样。 它具有与外部完全相同的可观察效果(pfff) *

以及两者的好处。

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

*见评论。

就个人而言,我更喜欢object.nil? 因为在较长的路线上它可能不那么令人困惑; 不过,我也经常使用object.blank? 如果我在Rails中工作,那么也会检查variables是否为空。

除了语法和风格外,我想看看“相同”的各种方法来testing零。 于是,我写了一些基准来看,并对其进行各种forms的零检验。

TL; DR – 结果第一

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

令人惊讶的是, obj执行速度是obj == nil变化的3-4倍,对此似乎有惩罚性能的惩罚。

想要将性能密集型algorithm加速200%-300%? 将所有obj == null检查转换为obj 。 想要沙袋你的代码的性能? 在任何地方都可以使用obj == null 。 (只是在开玩笑:不要沙包你的代码!)。

说到底,总是使用obj 。 这与Ruby样式指南规则jive: 不要做明确的非零检查,除非你正在处理布尔值。

基准条件

好的,那么这些就是结果,那么这个基准是如何结合在一起的,做了哪些testing以及结果的细节是什么?

我提出的零检查是:

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

我select了各种Rubytypes来testing,以防结果根据types而改变。 这些types是Fixnum,Float,FalseClass,TrueClass,String和Regex

我在每种types上都使用了这些无检查条件,以查看它们之间是否存在差异,性能方面。 对于每种types,我都testing了nil对象和非nil值对象(例如1_000_000falsetrue"string"/\w/ ),以查看是否在对象上检查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和Stringtypes的性能几乎相同,正则expression式差不多,而FalseClass和TrueClassperformance得更快。 在MRI版本1.9.3,2.0.0,2.1.5和2.2.5上进行testing,在各版本中具有非常相似的比较结果。 这里显示了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) 

在许多情况下,只要testing布尔值的真值

虽然这两个操作是非常不同的,我敢肯定,他们将始终产生相同的结果,至less直到有人在某个边缘决定重写对象的#nil? 方法。 (一个调用从Objectinheritance的#nil?方法,或者在NilClass重写, NilClassnil单例进行比较。)

我会build议,如果有疑问,你可以采取第三种方式,实际上只是testingexpression式的真值。

那么, if x和不是, if x == nilif x.nil? ,以便在expression式值为false时进行DTRTtesting。 这样工作也可能有助于避免诱惑某人定义FalseClass#nil? 真实的

你可以在nil?上使用Symbol#to_proc nil? ,而对于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 

我发现自己不使用.nil? 完全可以做到的时候:

 unless obj // do work end 

它实际上使用.nil?较慢.nil? 但并不明显。 .nil? 只是一个方法来检查,如果该对象是否等于零,除了视觉上诉和很less的performance,没有什么区别。

有人可能会build议使用.nil? 比简单的比较慢,这在你思考时是有意义的。

但如果规模和速度不是你所关心的,那么。 也许更具可读性。