什么时候应该使用Struct和OpenStruct?

一般来说,与Struct相比,使用OpenStruct有哪些优缺点? 什么types的一般用例适合每一种?

使用OpenStruct ,您可以任意创build属性。 另一方面, Struct在创build时必须定义其属性。 相互之间的select主要应基于是否需要稍后添加属性。

思考它们的方式是哈希在一方和另一方之间的中间地带。 它们意味着数据之间的一个更具体的关系Hash ,但他们没有类的实例方法。 例如,一堆函数的选项在散列中是有意义的; 他们只是松散的相关。 函数所需的名称,电子邮件和电话号码可以一起打包在StructOpenStruct 。 如果该名称,电子邮件和电话号码需要使用“First Last”和“Last,First”格式提供名称的方法,则应该创build一个类来处理它。

其他基准:

 require 'benchmark' require 'ostruct' REP = 100000 User = Struct.new(:name, :age) USER = "User".freeze AGE = 21 HASH = {:name => USER, :age => AGE}.freeze Benchmark.bm 20 do |x| x.report 'OpenStruct slow' do REP.times do |index| OpenStruct.new(:name => "User", :age => 21) end end x.report 'OpenStruct fast' do REP.times do |index| OpenStruct.new(HASH) end end x.report 'Struct slow' do REP.times do |index| User.new("User", 21) end end x.report 'Struct fast' do REP.times do |index| User.new(USER, AGE) end end end 

对于那些不想自己运行的基准testing结果的想法,这里是上面的代码的输出(在MB Pro 2.4GHz i7上)

  user system total real OpenStruct slow 4.430000 0.250000 4.680000 ( 4.683851) OpenStruct fast 4.380000 0.270000 4.650000 ( 4.649809) Struct slow 0.090000 0.000000 0.090000 ( 0.094136) Struct fast 0.080000 0.000000 0.080000 ( 0.078940) 

更新:

从Ruby 2.4.1开始,OpenStruct和Struct的速度非常接近。 请参阅https://stackoverflow.com/a/43987844/128421

先前:

为了完整性: 结构哈希OpenStruct

在Ruby 1.9.2上运行类似于burtlo的代码(4个x86_64内核中的1个,8GB RAM)[编辑表以alignment列]:

创build1个Mio Structs:1.43秒,219 MB / 90MB(virt / res)
创build1个Mio类实例:1.43秒,219 MB / 90MB(virt / res)
创build1个Mio Hashes:4.46秒,493 MB / 364MB(virt / res)
创build1个Mio OpenStructs:415.13秒,2464 MB / 2.3GB(virt / res),比散列慢100倍
创build100K OpenStructs:10.96秒,369 MB / 242MB(virt / res)

OpenStructs是慢慢的内存密集的 ,并且不能很好地适应大型数据集

创build1个Mio OpenStructs 创build1个Mio Hashes 慢100倍

 start = Time.now collection = (1..10**6).collect do |i| {:name => "User" , :age => 21} end; 1 stop = Time.now puts "#{stop - start} seconds elapsed" 

两者的用例是完全不同的。

您可以将Ruby 1.9中的Struct类视为等同于C中的struct声明。在Ruby中, Struct.new将一组字段名称作为参数并返回一个新的Class。 类似地,在C语言中,一个struct声明需要一组字段,并允许程序员像使用任何内置types一样使用新的复杂types。

ruby:

 Newtype = Struct.new(:data1, :data2) n = Newtype.new 

C:

 typedef struct { int data1; char data2; } newtype; newtype n; 

OpenStruct类可以与C中的匿名结构声明进行比较。它允许程序员创build一个复杂types的实例

ruby:

 o = OpenStruct.new(data1: 0, data2: 0) o.data1 = 1 o.data2 = 2 

C:

 struct { int data1; char data2; } o; o.data1 = 1; o.data2 = 2; 

这里有一些常见的用例。

可以使用OpenStructs轻松地将散列转换为响应所有散列键的一次性对象。

 h = { a: 1, b: 2 } o = OpenStruct.new(h) oa = 1 ob = 2 

对于速记类定义,结构体可能是有用的。

 class MyClass < Struct.new(:a,:b,:c) end m = MyClass.new ma = 1 

OpenStructs使用更多的内存,比Structs更慢。

 require 'ostruct' collection = (1..100000).collect do |index| OpenStruct.new(:name => "User", :age => 21) end 

在我的系统上,下面的代码在14秒内执行并消耗1.5 GB的内存。 你的里程可能不一样:

 User = Struct.new(:name, :age) collection = (1..100000).collect do |index| User.new("User",21) end 

这几乎立即完成,消耗26.6 MB的内存。

看看这个新方法的API。 在那里可以find很多差异。

就我个人而言,我非常喜欢OpenStruct,因为我不必事先定义对象的结构,只需添加我想要的东西。 我想这将是它的主要优点?

使用@Robert的代码,我添加Hashie :: Mash的基准项目,并得到了这样的结果:

  user system total real Hashie::Mash slow 3.600000 0.000000 3.600000 ( 3.755142) Hashie::Mash fast 3.000000 0.000000 3.000000 ( 3.318067) OpenStruct slow 11.200000 0.010000 11.210000 ( 12.095004) OpenStruct fast 10.900000 0.000000 10.900000 ( 12.669553) Struct slow 0.370000 0.000000 0.370000 ( 0.470550) Struct fast 0.140000 0.000000 0.140000 ( 0.145161) 

Struct

 >> s = Struct.new(:a, :b).new(1, 2) => #<struct a=1, b=2> >> sa => 1 >> sb => 2 >> sc NoMethodError: undefined method `c` for #<struct a=1, b=2> 

OpenStruct

 >> require 'ostruct' => true >> os = OpenStruct.new(a: 1, b: 2) => #<OpenStruct a=1, b=2> >> os.a => 1 >> os.b => 2 >> os.c => nil