2016-08-28 9 views
2

複雑なグラフ(How do you make a deep copy of an object in Java?など)を使用してオブジェクトをディープコピーする方法として、シリアライゼーションとデシリアライズが推奨されています。コピーコンストラクタ/ファクトリメソッドのアプローチは適切ではありません。初期容量の地図をどのようにディープコピーするのですか?

ただし、この方法は、初期容量を指定したマップでは機能しません。私のこれまでの質問(Why does specifying Map's initial capacity cause subsequent serializations to give different results?)は、結果のオブジェクトが等しくないことを示しており、その答えがバイト表現が異なることを示しています。シリアル化がbyte[] b1ならば、デシリアライズとシリアライズはb1と少なくとも1要素が異なる)。これは、オブジェクトの直列化/削除の通常の動作とは対照的です。

ド/シリアル化プロセスを制御readObjectwriteObject方法はprivateあるので、オーバーライドすることができない - これはおそらく目的(Hashmap slower after deserialization - Why?)で行いました。

マップを含む他の多くのオブジェクトを保持するオブジェクトに対して、デシリアライゼーションディープコピーアプローチを使用しています。また、バイト配列表現の比較と変更も行っています。マップが初期容量パラメータで初期化されていない限り、すべて正常に動作します。しかし、初期容量を指定してマップを最適化しようとすると、前述のようにこのアプローチが破られます。

この問題を回避することが可能かどうか、もしそうなら、私は知りたいと思います。

+3

「アプローチを破る」のはなぜですか?容量が異なることについて「壊れている」とは何ですか? 'HashMap'の反復順序は、*具体的には*任意であると定義されているので、反復順序は言いません。 – Andreas

+0

私はAndreasと同じ質問があります。このアプローチでどのような問題が発生しているのかを明確にすることはできますか?そして、実際には、小さな代表例*が本当にここで助けます。 –

+0

また、簡単な提案と同じように、あなたはチャンスを持っています。 ['readResolve() '](https://docs.oracle.com/javase/7/docs/platform/serialization/spec/input.html#5903)のデシリアライズ後にマップなどを再構築してください。したがって、あなたが本当に壊れていると仮定し、それが別の問題ではなく、別のアプローチを完全に排除すると、 'readResolve()'に適切なパラメータを使ってマップを再構築する可能性があります。 –

答えて

1

最初に、最初の容量を指定するとシリアル化された別のバイトにつながるという事実に焦点を当て、間違ったツリーを作り上げています。実際には、違いを見ると、

 
pbA from your example: 
: ac ed 00 05 73 72 00 0f 71 33 39 31 39 33 34 39 ....sr..q3919349 
: 34 2e 53 74 61 74 65 00 00 00 00 00 00 00 01 02 4.State......... 
: 00 01 4c 00 04 6d 61 70 73 74 00 10 4c 6a 61 76 ..L..mapst..Ljav 
: 61 2f 75 74 69 6c 2f 4c 69 73 74 3b 78 70 73 72 a/util/List;xpsr 
: 00 13 6a 61 76 61 2e 75 74 69 6c 2e 41 72 72 61 ..java.util.Arra 
: 79 4c 69 73 74 78 81 d2 1d 99 c7 61 9d 03 00 01 yListx.....a.... 
: 49 00 04 73 69 7a 65 78 70 00 00 00 01 77 04 00 I..sizexp....w.. 
: 00 00 01 73 72 00 14 71 33 39 31 39 33 34 39 34 ...sr..q39193494 
: 2e 4d 61 70 57 72 61 70 70 65 72 00 00 00 00 00 .MapWrapper..... 
: 00 00 01 02 00 01 4c 00 03 6d 61 70 74 00 0f 4c ......L..mapt..L 
: 6a 61 76 61 2f 75 74 69 6c 2f 4d 61 70 3b 78 70 java/util/Map;xp 
: 73 72 00 11 6a 61 76 61 2e 75 74 69 6c 2e 48 61 sr..java.util.Ha 
: 73 68 4d 61 70 05 07 da c1 c3 16 60 d1 03 00 02 shMap......`.... 
: 46 00 0a 6c 6f 61 64 46 61 63 74 6f 72 49 00 09 F..loadFactorI.. 
: 74 68 72 65 73 68 6f 6c 64 78 70 3f 40 00 00 00 [email protected] 
: 00 00 02 77 08 00 00 00 02 00 00 00 00 78 78  ...w.........xx 

zero from your example: 
: ac ed 00 05 73 72 00 0f 71 33 39 31 39 33 34 39 ....sr..q3919349 
: 34 2e 53 74 61 74 65 00 00 00 00 00 00 00 01 02 4.State......... 
: 00 01 4c 00 04 6d 61 70 73 74 00 10 4c 6a 61 76 ..L..mapst..Ljav 
: 61 2f 75 74 69 6c 2f 4c 69 73 74 3b 78 70 73 72 a/util/List;xpsr 
: 00 13 6a 61 76 61 2e 75 74 69 6c 2e 41 72 72 61 ..java.util.Arra 
: 79 4c 69 73 74 78 81 d2 1d 99 c7 61 9d 03 00 01 yListx.....a.... 
: 49 00 04 73 69 7a 65 78 70 00 00 00 01 77 04 00 I..sizexp....w.. 
: 00 00 01 73 72 00 14 71 33 39 31 39 33 34 39 34 ...sr..q39193494 
: 2e 4d 61 70 57 72 61 70 70 65 72 00 00 00 00 00 .MapWrapper..... 
: 00 00 01 02 00 01 4c 00 03 6d 61 70 74 00 0f 4c ......L..mapt..L 
: 6a 61 76 61 2f 75 74 69 6c 2f 4d 61 70 3b 78 70 java/util/Map;xp 
: 73 72 00 11 6a 61 76 61 2e 75 74 69 6c 2e 48 61 sr..java.util.Ha 
: 73 68 4d 61 70 05 07 da c1 c3 16 60 d1 03 00 02 shMap......`.... 
: 46 00 0a 6c 6f 61 64 46 61 63 74 6f 72 49 00 09 F..loadFactorI.. 
: 74 68 72 65 73 68 6f 6c 64 78 70 3f 40 00 00 00 [email protected] 
: 00 00 00 77 08 00 00 00 01 00 00 00 00 78 78  ...w.........xx 

唯一の違いはthe couple of bytes that specify load factor and suchです。明らかに、これらのバイトはコースのものとは異なります。最初のデシリアライゼーションで無視された別の初期容量を指定するとします。 これは赤いニシンです。

壊れたディープコピーが心配ですが、この懸念は間違っています。 正しさの点で重要なのは、逆シリアル化の結果だけです。これは、プログラムのインバリアントに違反しない、完全で機能的な正しいディープコピーである必要があります。正確なシリアライズされたバイトに焦点を当てることは注意散漫です。あなたはそれらについて気にしません。実際は、結果が正しいことだけを気にします。次のポイントに私たちをもたらします

:あなたがここに直面

のみ本当の問題は、長期性能に差(速度とメモリの両方)いくつかのJavaのバージョンは、初期マップを無視しているという事実から特性でありますデシリアライズ時の容量これはデータには影響しません(つまり、不変量は破られません)。

だからあなた非常に最初のステップが、これは実際に問題であることを確認にあります。つまり、早急な最適化問題が発生する可能性があります。現在、非直列化マップの初期容量の違いを無視します。あなたのアプリケーションが十分な性能特性で動作するなら、あなたは心配する必要がありません。そうでない場合、および異なる初期容量のために、逆シリアル化されたハッシュマップのパフォーマンスが低下するようにボトルネックを絞り込める場合は、の方がよいでしょうか。

したがって、の場合、逆シリアル化マップのパフォーマンス特性が実際には不十分であると判断した場合、実行できることがいくつかあります。適切なパラメータを使用して新しいマップ(初期容量など)を構築し

  1. 私は考えることができる最も簡単な、最も明白な一つは、あなたのオブジェクトにreadResolve()を実装、およびにその機会を利用しています

  2. 古い非直列化マップのすべてのアイテムを新しいマップにコピーします。
  3. 古いマップを破棄し、新しい地図と置き換えます。

例(元のコード例から、「偽」の結果をもたらしたマップを選択する):

class MapWrapper implements Serializable { 

    private static final long serialVersionUID = 1L; 

    Map<String, Integer> map = new HashMap<>(2); 

    private Object readResolve() throws ObjectStreamException { 
     // Replace deserialized 'map' with one that has the desired 
     // capacity parameters. 
     Map<String, Integer> fixedMap = new HashMap<>(2); 
     fixedMap.putAll(map); 
     map = fixedMap; 
     return this; 
    } 

} 

しかし、最初の質問、これは本当にあなたのための問題を引き起こしている場合。私はあなたがそれを思い知らされていると信じており、byte-for-byteシリアル化されたデータの比較に焦点を当てることはあなたにとって生産的ではありません。

+0

パフォーマンスの問題だけではありません。私のコメント返信が示すように例外がスローされます。そのためのコードを提供する必要がありますか? – user1803551

+1

@ user1803551はい。 –

関連する問題