浅いコピー (shallow copy) なら Hash#clone や Array#clone でいいけど、完全にコピーして別物として操作したい場合は Marshal.load(Marshal.dump(Object)) で 深いコピー (deep copy) をする。

サンプルコード。
Hash と Array の入れ子を作って、シャローコピーとディープコピーしていじってみた結果。


#!/usr/bin/env ruby
 
require 'rubygems'
require 'json'
 
data1 = {
  'key1' => 'hoge1',
  'hash1' => {
    'key2' => 'hoge2',
    'array1' => ['hello', 'goodbye', {'a' => 'b'}],
  }
}
 
# shallow copy
data2 = data1.clone()
 
# deep copy
data3 = Marshal.load(Marshal.dump(data1))
 
data1['key1'] = 'foobar1'
data1['hash1']['key2'] = 'foobar2'
data1['hash1']['array1'][1] = 'sayonara'
 
print "\n----------------------------------------\n"
print JSON.pretty_generate(data1)
print "\n----------------------------------------\n"
print JSON.pretty_generate(data2)
print "\n----------------------------------------\n"
print JSON.pretty_generate(data3)
print "\n----------------------------------------\n"

実行結果。
浅いコピー data2 のほうは最初の階層の key1 が持つ文字列だけ独立しているけど、他は data1 の変更につられる。
深いコピー data3 のほうは完全に別物なので、data1 の変更に引きずられない。


$ ruby ./clone.rb
 
----------------------------------------
{
  "hash1": {
    "array1": [
      "hello",
      "sayonara",
      {
        "a": "b"
      }
    ],
    "key2": "foobar2"
  },
  "key1": "foobar1"
}
----------------------------------------
{
  "hash1": {
    "array1": [
      "hello",
      "sayonara",
      {
        "a": "b"
      }
    ],
    "key2": "foobar2"
  },
  "key1": "hoge1"
}
----------------------------------------
{
  "hash1": {
    "array1": [
      "hello",
      "goodbye",
      {
        "a": "b"
      }
    ],
    "key2": "hoge2"
  },
  "key1": "hoge1"
}
----------------------------------------
clone

dup

 レシーバと同じ内容を持つ新しいハッシュを返します。フリーズしたハッシュの clone は同様にフリーズされたハッシュを返しますが、 dup は内容の等しいフリーズされていないハッシュを返します。

Hash - Rubyリファレンスマニュアル
clone

dup

 レシーバと同じ内容を持つ新しい配列を返します。clone は frozen tainted singleton-class の情報も含めてコピーしますが、 dup は内容だけをコピーします。

 またどちらのメソッドも要素それ自体のコピーはしません。つまり「浅い(shallow)」コピーを行います。

Array - Rubyリファレンスマニュアル
clone

dup

 オブジェクトの複製を作成して返します。

 clone は freeze、taint、特異メソッドなどの情報も含めた完全な複製を、dup はオブジェクトの内容のみの複製を作ります。

 clone や dup は「浅い(shallow)」コピーであることに注意してください。オブジェクト自身を複製するだけで、オブジェクトの指している先(たとえば配列の要素など)までは複製しません。 *1

(中略)

*1 深い(deep)コピーが必要な場合には、 Marshal.load(Marshal.dump(obj)を使ってください。ただしMarshal出来ないオブジェクトが含まれている場合には使えません。

Object - Rubyリファレンスマニュアル

ちなみに、環境。


$ uname -mrsv
Darwin 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun  7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386
 
$ ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]

tags: ruby

Posted by NI-Lab. (@nilab)