LoginSignup
39
26

More than 5 years have passed since last update.

Rubyにおけるsize, length, countの違いについて

Last updated at Posted at 2016-12-20

配列などの集合インスタンスの要素数を取得する時に #size #length または #countを使っているコードをよく見ます。よく見るが故に、違いがなんなのか気になったので調査・検証してみました。

結論

  • #size, #length はレシーバがすでに持っている要素数情報を参照する
  • #countはEnumerableモジュールで定義されているメソッドで、実際にeach処理を回してカウントした結果を出力している

よって、まとめると 要素数が多いインスタンスで count を実行すると遅くなる

解説

実際に計測してみた

こんなコードを書いて検証してみた。
100万の要素数を持つ配列を使って、それぞれ100万回繰り返して各メソッドを実行してみました。

require 'benchmark'

num = 1_000_000
a = []
num.times.to_a

Benchmark.bm 10 do |r|
  r.report '#size' do
    num.times do
       a.size
    end
  end

  r.report '#length' do
    num.times do
       a.length
    end
  end

  r.report '#count' do
    num.times do
       a.count
    end
  end
end

結果がこのようになりました。

1回目
                 user     system      total        real
#size        0.060000   0.000000   0.060000 (  0.066027)
                 user     system      total        real
#length      0.050000   0.000000   0.050000 (  0.054165)
                 user     system      total        real
#count       0.080000   0.000000   0.080000 (  0.080645)
2回目
                 user     system      total        real
#size        0.050000   0.000000   0.050000 (  0.051824)
                 user     system      total        real
#length      0.050000   0.000000   0.050000 (  0.055952)
                 user     system      total        real
#count       0.070000   0.000000   0.070000 (  0.081042)
3回目
                 user     system      total        real
#size        0.050000   0.000000   0.050000 (  0.054816)
                 user     system      total        real
#length      0.050000   0.010000   0.060000 (  0.056448)
                 user     system      total        real
#count       0.080000   0.000000   0.080000 (  0.085763)

以降、繰り返ししても #count が遅いことに変わりはありませんでした。

なぜ #count は遅いのか?

#size#length はレシーバの持っている要素情報を参照しているから早いです。一方で #count#each を実行して数え上げているので、前者と比べて余計な処理をしていることになる。なので、時間がかかってしまいます。

#count は使わないほうがよいのか?

いえ、そういうわけではありません。結果をみると、わずかに遅いだけなので致命的な問題になることはあまりないでしょう。また、繰り返し処理を実行しているということが役立つときもあります。例えば、条件に一致した要素だけ数えあげる、といった場合などですね。

a = [1,3,5,7,9,11,13,17,19,23].count { |i| i > 9 }
puts a # 5

#count はブロック引数を持たせることができるので、このような抽出が可能なのです。
ただし、単純に要素数を参照したいだけであれば #size, #length を使うのが好ましいですね。

39
26
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
26