关于ruby:何时应该使用Struct vs. OpenStruct?

When should I use Struct vs. OpenStruct?

通常,与Struct相比,使用OpenStruct有哪些优点和缺点? 哪种类型的一般用例适合其中的每一种?


使用OpenStruct,您可以任意创建属性。另一方面,Struct在创建时必须定义其属性。一个选择另一个应该主要基于您是否以后需要添加属性。

考虑它们的方法是,一方面是哈希值,另一方面是类之间??的频谱中间点。它们暗示数据之间的关系比Hash更具体,但它们不像类那样具有实例方法。例如,函数的一堆选项在哈希中有意义。它们只是松散相关。函数所需的名称,电子邮件和电话号码可以打包在StructOpenStruct中。如果该名称,电子邮件和电话号码需要使用" First Last"和" Last,First"两种格式提供名称的方法,则应创建一个类来处理它。


其他基准:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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

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

1
2
3
4
5
                          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

先前:

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

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

1
2
3
4
5
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 Hashes
creating 100K OpenStructs :     10.96 sec ,  369 MB / 242MB (virt/res)

OpenStructs占用大量内存,并且不能很好地扩展大型数据集

创建1个Mio OpenStructs比创建1个Mio哈希要慢100倍。

1
2
3
4
5
6
7
8
9
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声明采用一组字段,并允许程序员像使用任何内置类型一样使用新的复杂类型。

红宝石:

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

C:

1
2
3
4
5
6
typedef struct {
  int data1;
  char data2;
} newtype;

newtype n;

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

红宝石:

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

C:

1
2
3
4
5
6
7
struct {
  int data1;
  char data2;
} o;

o.data1 = 1;
o.data2 = 2;

以下是一些常见的用例。

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

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

结构对于速写类定义很有用。

1
2
3
4
5
class MyClass < Struct.new(:a,:b,:c)
end

m = MyClass.new
m.a = 1


与Structs相比,OpenStructs使用大量内存,并且性能较慢。

1
2
3
4
5
require 'ostruct'

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

在我的系统上,以下代码在14秒内执行,并消耗了1.5 GB的内存。您的里程可能会有所不同:

1
2
3
4
5
User = Struct.new(:name, :age)

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

该操作几乎立即完成,并消耗了26.6 MB的内存。


Struct

1
2
3
4
5
6
7
8
>> s = Struct.new(:a, :b).new(1, 2)
=> #<struct a=1, b=2>
>> s.a
=> 1
>> s.b
=> 2
>> s.c
NoMethodError: undefined method `c` for #<struct a=1, b=2>

OpenStruct

1
2
3
4
5
6
7
8
9
10
>> require 'ostruct'
=> true
>> os = OpenStruct.new(a: 1, b: 2)
=> #<OpenStruct a=1, b=2>
>> os.a
=> 1
>> os.b
=> 2
>> os.c
=> nil


看一下有关新方法的API。在此可以找到很多不同之处。

就我个人而言,我非常喜欢OpenStruct,因为我不必事先定义对象的结构,只需添加所需的内容即可。我猜这将是它的主要(不利)优势吗?

  • http://www.ruby-doc.org/core/classes/Struct.html
  • http://www.ruby-doc.org/stdlib/libdoc/ostruct/rdoc/classes/OpenStruct.html

使用@Robert代码,我将Hashie :: Mash添加到基准项并获得以下结果:

1
2
3
4
5
6
7
                           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)