使用 obj.nil?或 obj == nil是否更好? 两者的好处是什么?
obj.nil?
obj == nil
就个人而言,我更喜欢 object.nil?,因为它在较长的代码行上不那么令人困惑; 但是,如果我在 Rails 中工作,我通常也会使用 object.blank?,因为它也会检查变量是否为空。
object.nil?
object.blank?
我发现自己根本不使用 .nil?当你可以做:
.nil?
unless obj // do work end
实际上使用 .nil?会慢一些,但不会明显。.nil?只是一个检查对象是否等于零的方法,除了视觉上的吸引力和它所需要的很少的性能没有什么区别。
一些 可能建议使用. nil? 比简单的比较慢,这在您考虑时是有意义的。
但是,如果您不关心规模和速度,那么.nil? 可能更具可读性。
使用 objec.nil 或 obj = = nil 更好吗
它具有从外部观察到的完全相同的效果(pfff) < sup > *
这两者的好处是什么。
如果您喜欢微优化 所有,那么除了对象 nil本身之外,对象将返回 false到 .nil?消息,而使用 ==消息的对象将执行一个微小的微比较 与另一个对象确定它是否是同一个对象。
nil
false
==
* 查看评论。
您可以在 nil?上使用符号 # to _ proc,而在 x == nil上则不实用。
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
虽然这两个操作有很大的不同,但我确信它们总是会产生相同的结果,至少在某些边缘的人决定覆盖 Object 的 #nil?方法之前是如此。(一个调用从 Object 继承的 #nil?方法或在 NilClass中重写的方法,另一个调用与 nil单例进行比较。)
#nil?
NilClass
我建议,当你有疑问的时候,你可以选择第三种方式,实际上,只是测试一个表达式的真值。
因此,if x而不是 if x == nil或 if x.nil?,以便在表达式值为 假的时进行此测试 DTRT。以这种方式工作也可能有助于避免诱惑别人将 FalseClass#nil?定义为 没错。
if x
if x == nil
if x.nil?
FalseClass#nil?
撇开语法和样式不谈,我想看看不同的 nil 测试方法是如何“相同”的。因此,我编写了一些基准来查看,并对其进行了各种形式的 null 测试。
实际结果表明,使用 obj作为空检查在所有情况下都是最快的。obj始终比检查 obj.nil?快30% 或更多。
obj
令人惊讶的是,obj的执行速度大约是 obj == nil的3-4倍,对此似乎有一个惩罚性的性能损失。
想要将性能密集型算法的速度提高200% -300% 吗?将所有 obj == nil检查转换为 obj。想要阻碍代码的性能吗?尽可能使用 obj == nil。(只是开个玩笑: 不要把你的代码放在沙袋里!).
归根结底,始终使用 obj。这与 红宝石风格指南规则: 除非处理的是布尔值,否则不要执行显式的非零检查。是一致的
好了,结果出来了。那么,这个基准是如何组合在一起的,完成了哪些测试,结果的细节是什么?
我想到的空头支票是:
!obj
!!obj
obj != nil
我选择了各种 Ruby 类型进行测试,以防结果根据类型发生变化。这些类型是 Fixnum、 Float、 FalseClass、 TrueClass、 String和 Regex。
Fixnum
Float
FalseClass
TrueClass
String
Regex
我对每种类型都使用了这些 nil 检查条件,以查看它们之间在性能方面是否存在差异。对于每种类型,我都测试了 nil 对象和非 nil 值对象(例如 1_000_000、 100_000.0、 false、 true、 "string"和 /\w/) ,看看在一个为 nil 的对象上检查 nil 与在一个不为 nil 的对象上检查 nil 是否有区别。
1_000_000
100_000.0
true
"string"
/\w/
所有这些问题都解决了,下面是基准代码:
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)