This use case is under conditions that the array size is huge and the kind of elements are limited.
irb(main):001:0> require 'objspace'
=> true
irb(main):002:0> ObjectSpace.memsize_of(Array.new(16384))
=> 131112
irb(main):003:0> s = '0' * 16384
=> "00000000000000000000000000000000000...
irb(main):004:0> ObjectSpace.memsize_of(String.new(s, encoding: Encoding::BINARY, capacity: 16384))
=> 16425
irb(main):005:0> ObjectSpace.memsize_of(Array.new(256))
=> 2088
StringArray = Struct.new('StringArray', :string, :elements, keyword_init: true) do
def [](index)
raise IndexError if index < 0
return if index >= string.bytesize
elements[string.getbyte(index)]
end
def []=(index, element)
raise IndexError if index < 0
return if index >= string.bytesize
pos = elements.find_index(element) # O(N)
if pos.nil?
raise(RangeError, 'full of elements') if elements.size >= 256
pos = elements.size
elements << element
end
string.setbyte(index, pos)
end
end
FIXED_ARRAY_SIZE = 16384
BASE_STRING = '0' * FIXED_ARRAY_SIZE
base_elements = %w[a b c d e f g]
arr = if base_elements.size > 256
Array.new(FIXED_ARRAY_SIZE)
else
StringArray.new(
string: String.new(BASE_STRING, encoding: Encoding::BINARY, capacity: FIXED_ARRAY_SIZE),
elements: base_elements
)
end
FIXED_ARRAY_SIZE.times do { |i| arr[i] = base_elements.sample }
In certain use cases, it would be better to use struct instead of hash for memory efficiency.
irb(main):006:0> ObjectSpace.memsize_of({a:1, b:2, c:3, d:4, e:5})
=> 168
irb(main):007:0> Foo = Struct.new('Foo', :a, :b, :c, :d, :e, keyword_init: true)
=> Struct::Foo(keyword_init: true)
irb(main):008:0> ObjectSpace.memsize_of(Foo.new(a:1, b:2, c:3, d:4, e:5))
=> 80
https://nithinbekal.com/posts/bit-arrays-ruby/