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

时间:2022-09-10 23:22:29

In general, what are the advantages and disadvantages of using an OpenStruct as compared to a Struct? What type of general use-cases would fit each of these?

一般来说,使用OpenStruct与Struct相比有什么优点和缺点?什么样的通用用例适合每一个?

8 个解决方案

#1


152  

With an OpenStruct, you can arbitrarily create attributes. A Struct, on the other hand, must have its attributes defined when you create it. The choice of one over the other should be based primarily on whether you need to be able to add attributes later.

使用OpenStruct,您可以任意创建属性。另一方面,结构体在创建时必须定义其属性。两者之间的选择应该主要基于您是否需要稍后添加属性。

The way to think about them is as the middle ground of the spectrum between Hashes on one side and classes on the other. They imply a more concrete relationship amongst the data than does a Hash, but they don't have the instance methods as would a class. A bunch of options for a function, for example, make sense in a hash; they're only loosely related. A name, email, and phone number needed by a function could be packaged together in a Struct or OpenStruct. If that name, email, and phone number needed methods to provide the name in both "First Last" and "Last, First" formats, then you should create a class to handle it.

考虑它们的方法是把一边的散列和另一边的类作为光谱的中间点。它们在数据中意味着比散列更具体的关系,但是它们没有类的实例方法。例如,对于函数的一组选项,在散列中是有意义的;他们只有松散的联系。函数所需的名称、电子邮件和电话号码可以打包成Struct或OpenStruct。如果该名称、电子邮件和电话号码需要以“最后一”和“最后一”格式提供名称的方法,那么您应该创建一个类来处理它。

#2


78  

Other benchmark:

其他指标:

require 'benchmark'require 'ostruct'REP = 100000User = Struct.new(:name, :age)USER = "User".freezeAGE = 21HASH = {:name => USER, :age => AGE}.freezeBenchmark.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  endend

For the impatient who wants to get an idea of the benchmark results, without running them themselves, here is the output of the code above (on an MB Pro 2.4GHz i7)

对于那些不希望自己运行基准测试结果的不耐烦的人,下面是上面代码的输出(在MB Pro 2.4GHz i7上)

                          user     system      total        realOpenStruct 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)

#3


54  

UPDATE:

更新:

As of Ruby 2.4.1 OpenStruct and Struct are much closer in speed. See https://*.com/a/43987844/128421

在Ruby 2.4.1中,OpenStruct和Struct的速度更接近。参见https://*.com/a/43987844/128421

PREVIOUSLY:

以前:

For completeness: Struct vs. Class vs. Hash vs. OpenStruct

完整性:Struct vs. Class vs. Hash vs. OpenStruct

Running similar code as burtlo's, on Ruby 1.9.2, (1 of 4 cores x86_64, 8GB RAM) [table edited to align columns]:

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

creating 1 Mio Structs :         1.43 sec ,  219 MB /  90MB (virt/res)creating 1 Mio Class instances : 1.43 sec ,  219 MB /  90MB (virt/res)creating 1 Mio Hashes  :         4.46 sec ,  493 MB / 364MB (virt/res)creating 1 Mio OpenStructs :   415.13 sec , 2464 MB / 2.3GB (virt/res) # ~100x slower than Hashescreating 100K OpenStructs :     10.96 sec ,  369 MB / 242MB (virt/res)

OpenStructs are sloooooow and memory intensive , and don't scale well for large data sets

openstruct是sloooooow和内存密集型的,对于大型数据集不能很好地伸缩

Creating 1 Mio OpenStructs is ~100x slower than creating 1 Mio Hashes.

创建一个Mio OpenStructs要比创建一个Mio散列慢100倍。

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

#4


30  

The use cases for the two are quite different.

这两个用例是完全不同的。

You can think of the Struct class in Ruby 1.9 as an equivalent to the struct declaration in C. In Ruby Struct.new takes a set of field names as arguments and returns a new Class. Similarly, in C, a struct declaration takes a set of fields and allows the programmer to use the new complex type just like he would any built-in type.

您可以将Ruby 1.9中的Struct类看作与Ruby Struct中的c中的Struct声明等价。new以一组字段名作为参数,并返回一个新类。类似地,在C中,struct声明接受一组字段,并允许程序员使用新的复杂类型,就像使用任何内置类型一样。

Ruby:

Ruby:

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

C:

C:

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

The OpenStruct class can be compared to an anonymous struct declaration in C. It allows the programmer to create an instance of a complex type.

OpenStruct类可以与c中的匿名结构声明相比较,它允许程序员创建复杂类型的实例。

Ruby:

Ruby:

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

C:

C:

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

Here are some common use cases.

这里有一些常见的用例。

OpenStructs can be used to easily convert hashes to one-off objects which respond to all the hash keys.

openstruct可用于轻松地将哈希转换为一次性对象,该对象响应所有哈希键。

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

Structs can be useful for shorthand class definitions.

结构可以用于速记类定义。

class MyClass < Struct.new(:a,:b,:c)endm = MyClass.newm.a = 1

#5


23  

OpenStructs use significantly more memory and are slower performers versus Structs.

OpenStructs使用更多的内存,性能较慢。

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

On my system the following code executed in 14 seconds and consumed 1.5 GB of memory. Your mileage might vary:

在我的系统上,以下代码在14秒内执行,消耗了1.5 GB的内存。你的情况可能不同:

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

That finished nearly instantaneously and consumed 26.6 MB of memory.

这几乎是瞬间完成的,消耗了26.6 MB的内存。

#6


5  

Have a look at the API with regard to the new method. A lot of the differences can be found there.

请查看有关新方法的API。这里有很多不同之处。

Personally, I quite like OpenStruct, as I don't have to define the structure of the object beforehand, and just add stuff as I want. I guess that would be its main (dis)advantage?

我个人非常喜欢OpenStruct,因为我不需要预先定义对象的结构,只需要添加我想要的东西。我想那将是它的主要优势吗?

#7


3  

Using @Robert code, I add Hashie::Mash to the benchmark item and got this result:

使用@Robert代码,我将Hashie::Mash添加到基准项目中,得到如下结果:

                           user     system      total        realHashie::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)

#8


2  

Struct:

结构:

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

OpenStruct:

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

#1


152  

With an OpenStruct, you can arbitrarily create attributes. A Struct, on the other hand, must have its attributes defined when you create it. The choice of one over the other should be based primarily on whether you need to be able to add attributes later.

使用OpenStruct,您可以任意创建属性。另一方面,结构体在创建时必须定义其属性。两者之间的选择应该主要基于您是否需要稍后添加属性。

The way to think about them is as the middle ground of the spectrum between Hashes on one side and classes on the other. They imply a more concrete relationship amongst the data than does a Hash, but they don't have the instance methods as would a class. A bunch of options for a function, for example, make sense in a hash; they're only loosely related. A name, email, and phone number needed by a function could be packaged together in a Struct or OpenStruct. If that name, email, and phone number needed methods to provide the name in both "First Last" and "Last, First" formats, then you should create a class to handle it.

考虑它们的方法是把一边的散列和另一边的类作为光谱的中间点。它们在数据中意味着比散列更具体的关系,但是它们没有类的实例方法。例如,对于函数的一组选项,在散列中是有意义的;他们只有松散的联系。函数所需的名称、电子邮件和电话号码可以打包成Struct或OpenStruct。如果该名称、电子邮件和电话号码需要以“最后一”和“最后一”格式提供名称的方法,那么您应该创建一个类来处理它。

#2


78  

Other benchmark:

其他指标:

require 'benchmark'require 'ostruct'REP = 100000User = Struct.new(:name, :age)USER = "User".freezeAGE = 21HASH = {:name => USER, :age => AGE}.freezeBenchmark.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  endend

For the impatient who wants to get an idea of the benchmark results, without running them themselves, here is the output of the code above (on an MB Pro 2.4GHz i7)

对于那些不希望自己运行基准测试结果的不耐烦的人,下面是上面代码的输出(在MB Pro 2.4GHz i7上)

                          user     system      total        realOpenStruct 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)

#3


54  

UPDATE:

更新:

As of Ruby 2.4.1 OpenStruct and Struct are much closer in speed. See https://*.com/a/43987844/128421

在Ruby 2.4.1中,OpenStruct和Struct的速度更接近。参见https://*.com/a/43987844/128421

PREVIOUSLY:

以前:

For completeness: Struct vs. Class vs. Hash vs. OpenStruct

完整性:Struct vs. Class vs. Hash vs. OpenStruct

Running similar code as burtlo's, on Ruby 1.9.2, (1 of 4 cores x86_64, 8GB RAM) [table edited to align columns]:

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

creating 1 Mio Structs :         1.43 sec ,  219 MB /  90MB (virt/res)creating 1 Mio Class instances : 1.43 sec ,  219 MB /  90MB (virt/res)creating 1 Mio Hashes  :         4.46 sec ,  493 MB / 364MB (virt/res)creating 1 Mio OpenStructs :   415.13 sec , 2464 MB / 2.3GB (virt/res) # ~100x slower than Hashescreating 100K OpenStructs :     10.96 sec ,  369 MB / 242MB (virt/res)

OpenStructs are sloooooow and memory intensive , and don't scale well for large data sets

openstruct是sloooooow和内存密集型的,对于大型数据集不能很好地伸缩

Creating 1 Mio OpenStructs is ~100x slower than creating 1 Mio Hashes.

创建一个Mio OpenStructs要比创建一个Mio散列慢100倍。

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

#4


30  

The use cases for the two are quite different.

这两个用例是完全不同的。

You can think of the Struct class in Ruby 1.9 as an equivalent to the struct declaration in C. In Ruby Struct.new takes a set of field names as arguments and returns a new Class. Similarly, in C, a struct declaration takes a set of fields and allows the programmer to use the new complex type just like he would any built-in type.

您可以将Ruby 1.9中的Struct类看作与Ruby Struct中的c中的Struct声明等价。new以一组字段名作为参数,并返回一个新类。类似地,在C中,struct声明接受一组字段,并允许程序员使用新的复杂类型,就像使用任何内置类型一样。

Ruby:

Ruby:

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

C:

C:

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

The OpenStruct class can be compared to an anonymous struct declaration in C. It allows the programmer to create an instance of a complex type.

OpenStruct类可以与c中的匿名结构声明相比较,它允许程序员创建复杂类型的实例。

Ruby:

Ruby:

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

C:

C:

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

Here are some common use cases.

这里有一些常见的用例。

OpenStructs can be used to easily convert hashes to one-off objects which respond to all the hash keys.

openstruct可用于轻松地将哈希转换为一次性对象,该对象响应所有哈希键。

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

Structs can be useful for shorthand class definitions.

结构可以用于速记类定义。

class MyClass < Struct.new(:a,:b,:c)endm = MyClass.newm.a = 1

#5


23  

OpenStructs use significantly more memory and are slower performers versus Structs.

OpenStructs使用更多的内存,性能较慢。

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

On my system the following code executed in 14 seconds and consumed 1.5 GB of memory. Your mileage might vary:

在我的系统上,以下代码在14秒内执行,消耗了1.5 GB的内存。你的情况可能不同:

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

That finished nearly instantaneously and consumed 26.6 MB of memory.

这几乎是瞬间完成的,消耗了26.6 MB的内存。

#6


5  

Have a look at the API with regard to the new method. A lot of the differences can be found there.

请查看有关新方法的API。这里有很多不同之处。

Personally, I quite like OpenStruct, as I don't have to define the structure of the object beforehand, and just add stuff as I want. I guess that would be its main (dis)advantage?

我个人非常喜欢OpenStruct,因为我不需要预先定义对象的结构,只需要添加我想要的东西。我想那将是它的主要优势吗?

#7


3  

Using @Robert code, I add Hashie::Mash to the benchmark item and got this result:

使用@Robert代码,我将Hashie::Mash添加到基准项目中,得到如下结果:

                           user     system      total        realHashie::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)

#8


2  

Struct:

结构:

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

OpenStruct:

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