14

How are ruby arrays internally implemented (mainly in CRuby, but any other info is welcomed)?

Are they growable arrays like a c++ vector or are they list based? What's the complexity of shift/unshift and accessing an element by index?

1
  • 3
    Just checkout the SVN depo and read the source code. The implementation is not really hard for a C programmer. Commented Nov 12, 2012 at 13:46

2 Answers 2

18

They're growable arrays which "grow at the end".

shift is O(1), unshift is O(n) and accessing by index is O(1). To the best of my knowledge this holds true for all ruby implementations, but it definitely does in MRI.

UPDATE: After this answer was originally written, Ruby was enhanced to make unshift amortized O(1). The enhanced array is in Ruby 2.0.0 and later, making shift, unshift, push, and pop all O(1) or amortized O(1).

Sign up to request clarification or add additional context in comments.

3 Comments

I'm not sure shift is always O(N). If I recall correctly, the beginning of the memory block and the index of the first item are tracked separately, so you can do an O(1) shift by incrementing the firstItem index. Unshift is only O(N) if you havent done any shifts.
@AShelly: You seem to be right about shift (though it does not actually keep track of the starting index, but instead makes the array shared), but unshift definitely is O(n) - it calls memmove on the array no matter what.
unshift appears to be O(1) in ruby 2.2
1

unshift is O(N^2) in my ruby1.9 .

$ /usr/bin/time ruby -e 'n=100000;l=[];(1..n).each{|i| l.push(i);}'
        0.03 real         0.02 user         0.00 sys
$ /usr/bin/time ruby -e 'n=100000;l=[];(1..n).each{|i| l.unshift(i);}'
        3.15 real         3.11 user         0.01 sys
$ /usr/bin/time ruby -e 'n=200000;l=[];(1..n).each{|i| l.unshift(i);}'
       12.96 real        12.85 user         0.03 sys
$ ruby -v
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.3.0]

5 Comments

Correct me if I'm wrong but doesn't this show that unshift has quadratic time complexity? If unshift was linear, as you increase n from 100,000 to 200,000 wouldn't you expect the time taken to double. As n is increased by a factor of 2, the time taken to complete the algorithm is increased by a factor of 4. The number of operations is proportional to the size of the data-set squared.
@louism2 right... I fixed to O(N^2), and noticed that unshift() is O(N) in Ruby2, and O(N^2) in Ruby1.9 .
If unshift is O(n) and you call it n times it will take O(n^2) time.. also I imagine you want more than two data points for a trend..
You want something like n=100000;b=1000;l=(1..n).to_a;b.times{l.unshift(1);} (b stands for benchmark, keep b the same and varry n)
I'm getting roughly O(n) on ruby-1.9.3-p551 and interestingly O(1) on ruby-2.2.2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.