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_000
, false
, true
, "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
重写, NilClass
与nil
单例进行比较。)
我会build议,如果有疑问,你可以采取第三种方式,实际上只是testingexpression式的真值。
那么, if x
和不是, if x == nil
或if 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? 比简单的比较慢,这在你思考时是有意义的。
但如果规模和速度不是你所关心的,那么。 也许更具可读性。