什么时候在Ruby中使用Struct而不是Hash更好?

Ruby Struct允许使用一组访问器生成一个实例:

# Create a structure named by its constant Customer = Struct.new(:name, :address) #=> Customer Customer.new("Dave", "123 Main") #=> #<Customer name="Dave", address="123 Main"> 

这看起来方便而且function强大,但是,一个Hash可以做些非常相似的事情:

 Customer = {:name => "Dave", :address => "123 Main"} 

什么是现实世界的情况下,我应该更喜欢一个结构(以及为什么),以及在select一个之前有什么警告或陷阱?

就我个人而言,当我想使一块数据像一个数据集合而不是在一个Hash下松散耦合时,我使用了一个结构。

例如我做了一个脚本,从Youtube下载video,在那里我有一个结构来表示一个video,并testing是否所有的数据都在位:

 Video = Struct.new(:title, :video_id, :id) do def to_s "http://youtube.com/get_video.php?t=#{id}&video_id=#{video_id}&fmt=18" end def empty? @title.nil? and @video_id.nil? and @id.nil? end end 

后来在我的代码中,我有一个循环遍历video源HTML页面中的所有行,直到empty? 不会返回true。

我见过的另一个例子是James Edward Gray IIs configuration类 ,它使用OpenStruct轻松添加从外部文件加载的configurationvariables:

 #!/usr/bin/env ruby -wKU require "ostruct" module Config module_function def load_config_file(path) eval <<-END_CONFIG config = OpenStruct.new #{File.read(path)} config END_CONFIG end end # configuration_file.rb config.db = File.join(ENV['HOME'], '.cool-program.db') config.user = ENV['USER'] # Usage: Config = Config.load_config('configuration_file.rb') Config.db # => /home/ba/.cool-program.db Config.user # => ba Config.non_existant # => Nil 

StructOpenStruct的区别在于Struct只响应你设置的属性, OpenStruct响应任何属性集 – 但是没有设置值的属性将返回Nil

一个Struct可以通过索引和名字获得其元素:

 irb(main):004:0> Person = Struct.new(:name, :age) => Person irb(main):005:0> p = Person.new("fred", 26) => # irb(main):006:0> p[0] => "fred" irb(main):007:0> p[1] => 26 irb(main):008:0> p.name => "fred" irb(main):009:0> p.age => 26 

这有时是有用的。

主要是performance。 按大小顺序,结构要快得多。 与Hash或OpenStruct相比,消耗更less的内存。 更多信息: 什么时候应该使用Struct与OpenStruct?

关于使用Hashes的速度的评论,Struct或者OpenStruct:Hash总是会赢得一般的使用。 这是OpenStruct的基础,没有额外的结冰,所以它不那么灵活,但它是精益和平均的。

使用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 

使用更简洁的散列和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 

如果结构,哈希或者结构或者OpenStruct被定义了一次然后被使用了很多次,那么访问速度变得更加重要,并且结构开始发光:

 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 

但是请注意,Struct的访问速度只比Hash快4倍,而Hash的初始化,赋值和访问速度要快17倍。 您将不得不根据特定应用程序的需求找出最适合的方式。 我倾向于使用哈希作为结果的一般用途。

而且,多年来使用OpenStruct的速度大大提高, 它以前比基于我以前见过的基准testing结果要慢,并且比较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 

和:

 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 

和:

 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