When is it better to use a Struct rather than a Hash in Ruby?

前端 未结 6 1224
清歌不尽
清歌不尽 2021-01-30 20:44

A Ruby Struct allows an instance to be generated with a set of accessors:

# Create a structure named by its constant
Customer = Struct.new(:name, :address)     #         


        
6条回答
  •  鱼传尺愫
    2021-01-30 21:08

    Regarding comments about the speed of using Hashes, Struct or OpenStruct: Hash will always win for general use. It's the basis of OpenStruct without the additional icing so it's not as flexible, but it's lean and mean.

    Using Ruby 2.4.1:

    require 'fruity'
    require 'ostruct'
    
    def _hash
      h = {}
      h['a'] = 1
      h['a']
    end
    
    def _struct
      s = Struct.new(:a)
      foo = s.new(1)
      foo.a
    end
    
    def _ostruct
      person = OpenStruct.new
      person.a = 1
      person.a
    end
    
    compare do
      a_hash { _hash }
      a_struct { _struct }
      an_ostruct { _ostruct }
    end
    
    # >> Running each test 4096 times. Test will take about 2 seconds.
    # >> a_hash is faster than an_ostruct by 13x ± 1.0
    # >> an_ostruct is similar to a_struct
    

    Using more concise definitions of the hash and OpenStruct:

    require 'fruity'
    require 'ostruct'
    
    def _hash
      h = {'a' => 1}
      h['a']
    end
    
    def _struct
      s = Struct.new(:a)
      foo = s.new(1)
      foo.a
    end
    
    def _ostruct
      person = OpenStruct.new('a' => 1)
      person.a
    end
    
    compare do
      a_hash { _hash }
      a_struct { _struct }
      an_ostruct { _ostruct }
    end
    
    # >> Running each test 4096 times. Test will take about 2 seconds.
    # >> a_hash is faster than an_ostruct by 17x ± 10.0
    # >> an_ostruct is similar to a_struct
    

    If the structure, Hash or Struct or OpenStruct is defined once then used many times, then the access speed becomes more important, and a Struct begins to shine:

    require 'fruity'
    require 'ostruct'
    
    HSH = {'a' => 1}
    def _hash
      HSH['a']
    end
    
    STRCT = Struct.new(:a).new(1)
    def _struct
      STRCT.a
    end
    
    OSTRCT = OpenStruct.new('a' => 1)
    def _ostruct
      OSTRCT.a
    end
    
    puts "Ruby version: #{RUBY_VERSION}"
    
    compare do
      a_hash { _hash }
      a_struct { _struct }
      an_ostruct { _ostruct }
    end
    
    # >> Ruby version: 2.4.1
    # >> Running each test 65536 times. Test will take about 2 seconds.
    # >> a_struct is faster than a_hash by 4x ± 1.0
    # >> a_hash is similar to an_ostruct
    

    Notice though, that the Struct is only 4x faster than the Hash for accessing, whereas the Hash is 17x faster for initialization, assignment and accessing. You'll have to figure out which is the best to use based on the needs of a particular application. I tend to use Hashes for general use as a result.

    Also, the speed of using OpenStruct has improved greatly over the years; It used to be slower than Struct based on benchmarks I've seen in the past and comparing to 1.9.3-p551:

    require 'fruity'
    require 'ostruct'
    
    def _hash
      h = {}
      h['a'] = 1
      h['a']
    end
    
    def _struct
      s = Struct.new(:a)
      foo = s.new(1)
      foo.a
    end
    
    def _ostruct
      person = OpenStruct.new
      person.a = 1
      person.a
    end
    
    puts "Ruby version: #{RUBY_VERSION}"
    
    compare do
      a_hash { _hash }
      a_struct { _struct }
      an_ostruct { _ostruct }
    end
    
    # >> Ruby version: 1.9.3
    # >> Running each test 4096 times. Test will take about 2 seconds.
    # >> a_hash is faster than a_struct by 7x ± 1.0
    # >> a_struct is faster than an_ostruct by 2x ± 0.1
    

    and:

    require 'fruity'
    require 'ostruct'
    
    def _hash
      h = {'a' => 1}
      h['a']
    end
    
    def _struct
      s = Struct.new(:a)
      foo = s.new(1)
      foo.a
    end
    
    def _ostruct
      person = OpenStruct.new('a' => 1)
      person.a
    end
    
    puts "Ruby version: #{RUBY_VERSION}"
    
    compare do
      a_hash { _hash }
      a_struct { _struct }
      an_ostruct { _ostruct }
    end
    
    # >> Ruby version: 1.9.3
    # >> Running each test 4096 times. Test will take about 2 seconds.
    # >> a_hash is faster than a_struct by 7x ± 1.0
    # >> a_struct is faster than an_ostruct by 2x ± 1.0
    

    and:

    require 'fruity'
    require 'ostruct'
    
    HSH = {'a' => 1}
    def _hash
      HSH['a']
    end
    
    STRCT = Struct.new(:a).new(1)
    def _struct
      STRCT.a
    end
    
    OSTRCT = OpenStruct.new('a' => 1)
    def _ostruct
      OSTRCT.a
    end
    
    puts "Ruby version: #{RUBY_VERSION}"
    
    compare do
      a_hash { _hash }
      a_struct { _struct }
      an_ostruct { _ostruct }
    end
    
    # >> Ruby version: 1.9.3
    # >> Running each test 32768 times. Test will take about 1 second.
    # >> a_struct is faster than an_ostruct by 3x ± 1.0
    # >> an_ostruct is similar to a_hash
    

提交回复
热议问题