obj.nil? vs obj == nil

Est-il préférable d’utiliser obj.nil? ou obj == nil et quels sont les avantages des deux?

Est-il préférable d’utiliser obj.nil? ou obj == nil

c’est exactement le meme. Il a exactement les mêmes effets observables de l’extérieur (pfff) *

et quels sont les avantages des deux.

Si vous aimez les micro optimisations, tous les objects retourneront false au .nil? message à l’exception de l’object nil lui-même, tandis que l’object utilisant le message == effectuera une minuscule comparaison avec l’autre object pour déterminer s’il s’agit du même object.

* Voir les commentaires.

Personnellement, je préfère object.nil? comme il peut être moins déroutant sur les lignes plus longues; cependant, j’utilise aussi généralement object.blank? si je travaille dans Rails, cela vérifie également si la variable est vide.

Dans de nombreux cas, ni l’un ni l’autre, testez simplement la valeur de vérité booléenne

Bien que les deux opérations soient très différentes, je suis sûr qu’elles produiront toujours le même résultat, du moins jusqu’à ce que quelqu’un sur le bord de quelque chose décide de remplacer le #nil? l’ #nil? méthode. (On appelle la méthode #nil? de Object ou substituée dans NilClass et on compare à la singleton nil .)

Je suggérerais que, en cas de doute, vous franchissez une troisième voie et que vous testiez simplement la valeur de vérité d’une expression.

Donc, if x et pas if x == nil ou if x.nil? , afin d’avoir ce test DTRT lorsque la valeur de l’expression est fausse . Travailler de cette façon peut aussi aider à ne pas tenter de définir FalseClass#nil? comme vrai

Mis à part la syntaxe et le style, je voulais voir à quel point les différentes approches du test étaient nulles. Donc, j’ai écrit quelques repères pour voir et jeté différentes formes de tests nuls.

TL; DR – Résultats d’abord

Les résultats réels ont montré que l’utilisation de obj comme vérification nulle est la plus rapide dans tous les cas. obj est toujours plus rapide de 30% ou plus que de vérifier obj.nil? .

Étonnamment, obj effectue environ 3 à 4 fois plus vite que les variations sur obj == nil , pour lesquelles il semble y avoir une pénalité de performance.

Vous souhaitez accélérer votre algorithme à haute performance de 200% à 300%? Convertissez tous obj == null contrôles obj == null en obj . Vous voulez sabler les performances de votre code? Utilisez obj == null partout où vous le pouvez. (je rigole: ne salissez pas votre code!).

En dernière parsing, utilisez toujours obj . Cela correspond à la règle Ruby Style Guide : ne faites pas de vérifications explicites non nuls à moins de traiter des valeurs booléennes.

Les conditions de référence

OK, donc ce sont les résultats, alors comment est ce benchmark mis en place, quels tests ont été réalisés, et quels sont les détails des résultats?

Les chèques nuls que j’ai trouvés sont:

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

J’ai choisi différents types de Ruby à tester au cas où les résultats changeraient en fonction du type. Ces types étaient Fixnum, Float, FalseClass, TrueClass, Ssortingng et Regex

J’ai utilisé ces conditions de vérification nulles sur chacun des types pour voir s’il y avait une différence entre elles, du sharepoint vue des performances. Pour chaque type, j’ai testé à la fois des objects 1_000_000 et des objects de valeur non nulle (par exemple, 1_000_000 , 100_000.0 , false , true , "ssortingng" et /\w/ ) pour voir s’il y a une différence c’est nul contre un object qui n’est pas nul.

Les repères

Avec tout cela, voici le code de référence:

 require 'benchmark' nil_obj = nil N = 10_000_000 puts RUBY_DESCRIPTION [1_000_000, 100_000.0, false, true, "ssortingng", /\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 

Les resultats

Les résultats étaient intéressants, car les performances des types Fixnum, Float et Ssortingng étaient pratiquement identiques, Regex étant à peu près le même, et FalseClass et TrueClass fonctionnaient beaucoup plus rapidement. Les tests ont été effectués sur les versions 1.9.3, 2.0.0, 2.1.5 et 2.2.5 de l’IRM, avec des résultats comparatifs très similaires entre les versions. Les résultats de la version 2.2.5 de l’IRM sont affichés ici (et disponibles dans l’essentiel :

 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 = ssortingng (Ssortingng) 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) 

Vous pouvez utiliser Symbol # to_proc sur nil? , alors que ce ne serait pas pratique sur 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 

Je me trouve ne pas utiliser .nil? du tout quand vous pouvez faire:

 unless obj // do work end 

Il est en fait plus lent en utilisant .nil? mais pas sensiblement. .nil? est juste une méthode pour vérifier si cet object est égal à zéro, à part l’attrait visuel et très peu de performance, il n’y a pas de différence.

Certains pourraient suggérer que l’utilisation de .nil? est plus lent que la simple comparaison, ce qui a du sens quand on y pense.

Mais si l’échelle et la vitesse ne vous concernent pas, alors .nil? est peut-être plus lisible.