obj.nil?与 obj == nil

问题描述 投票:0回答:7

使用

obj.nil?
还是
obj == nil
更好?两者有什么好处?

ruby null comparison koan
7个回答
52
投票

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

完全一样。从外部观察到的效果完全相同(噗)*

两者有什么好处。

如果您喜欢微观优化所有,除了对象本身之外,所有对象都会将

false
返回到
.nil?
消息,而使用
nil
消息的对象将执行微小的微观比较 与另一个对象来确定它是否是同一个对象。

*

查看评论。


14
投票

TL;DR - 结果第一

实际结果表明,使用

==

作为 nil 检查是所有情况下最快的。

obj
始终比检查
obj
快 30% 或更多。

令人惊讶的是,

obj.nil?

的执行速度大约是

obj
变体的 3-4 倍,这似乎会带来惩罚性的性能损失。

想要将性能密集型算法的速度提高 200%-300%?将所有

obj == nil

检查转换为

obj == nil
。想要对代码的性能进行沙袋测试吗?尽可能使用
obj
。 (开玩笑:不要对你的代码进行沙袋处理!)。

归根结底,始终使用

obj == nil

。这符合

Ruby 风格指南
规则:不要进行显式的非零检查,除非您正在处理布尔值。 基准条件

好的,这就是结果。那么这个基准测试是如何组合在一起的,做了哪些测试,结果的详细信息是什么?

我想出的零检查是:

    obj
  • obj
  • obj.nil?
  • !obj
  • !!obj
  • obj == nil
  • 
    
    
  • 我选择了各种 Ruby 类型进行测试,以防结果因类型而变化。这些类型是
obj != nil

Fixnum
Float
FalseClass
TrueClass
String

我对每种类型使用了这些 nil 检查条件,以查看它们之间在性能方面是否存在差异。对于每种类型,我测试了 nil 对象和非 nil 值对象(例如

Regex

1_000_000
100_000.0
false
true
"string"
),看看检查是否存在差异for nil 在一个 nil 对象上与一个非 nil 对象上。

基准

完成所有这些后,这里是基准代码:

/\w/

结果

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

要点

中找到: 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



11
投票
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)

,因为它在较长的线路上不会那么混乱;但是,如果我在 Rails 中工作,我通常也会使用

object.nil?
,因为它还会检查变量是否为空。
    


5
投票
都不是,

只是测试布尔真值 虽然这两个操作非常不同,但我很确定它们总是会产生相同的结果,至少直到有人决定重写 Object 的

object.blank?

方法。 (一种调用从 Object 继承的

#nil?
方法,或者在
#nil?
中重写,然后与
NilClass
单例进行比较。)

我建议,当有疑问时,你实际上可以走第三条路,只是测试表达式的真值。

因此,使用

nil

而不是

if x
if x == nil
,以便在表达式值为
false
时进行此测试 DTRT。这种方式也可能有助于避免诱惑某人将 if x.nil? 定义为
true


3
投票
FalseClass#nil?

上使用 Symbol#to_proc,但在

nil?
上则不实用。

x == nil



0
投票
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 的方法,除了视觉吸引力和所需的很少的性能之外,没有任何区别。
    


-1
投票
有些

可能建议使用.nil?比简单的比较慢,仔细想想,这是有道理的。 但是,如果规模和速度不是您关心的问题,那么 .nil 呢?也许更具可读性。

© www.soinside.com 2019 - 2024. All rights reserved.