When should I use Struct vs. OpenStruct?
通常,与Struct相比,使用OpenStruct有哪些优点和缺点? 哪种类型的一般用例适合其中的每一种?
使用
考虑它们的方法是,一方面是哈希值,另一方面是类之间??的频谱中间点。它们暗示数据之间的关系比
其他基准:
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中的
红宝石:
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的内存。
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> |
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) |