Java Silverの学習中、配列とcloneメソッドの問題とその解説が全く理解できなかったので色々調べた結果、ようやく自分が納得できる形でまとめることができたので、情報整理も兼ねて投稿します。
悩みの種
次のコードを確認し、trueを返すコードとして正しいものを選びなさい。(一つ選択)
char[][] array1 = { { 'a' , 'b' } , { 'c' , 'd' } };
char[][] array2 = array1.clone();
char[] array3 = array1[1].clone();
A. array1[1] == array3
B. array1[1] == array3[1]
C. array1[1] == array2[1]
D. array1.equals(array2)
正解は...
C. array1[1] == array2[1]
解説(長すぎるので少し抜粋)
①Cloneメソッドは新しい配列を作り、その配列に同じ要素への参照をコピーする。
②Cloneメソッドで作られた複製の配列と元の配列は同じ参照先を共有する。
③array2 = array1.clone()
で、array1の参照先のコピーをarray2に代入している。
よって、array1とarray2は同じ要素の参照をしていることになり、array1の要素を変更すると、array2が参照している要素も変更される。
④同一性を検証する == 演算子はtrueと判定される。 よってCが正解になる。
⑤array1の2番目の要素(char配列)のcloneメソッドを使ったarray1[1].clone()
では、新しい配列を作り、その配列に同じ要素をコピーし直す。
新しい配列を作っているため、array1とarray3は同じ値を持っている異なる配列への参照を持つことになる。よってAは誤り。
⑥選択肢Bは二次元配列であるarray1の2番目の要素と、一次元配列であるarray3の2番目の要素を比較しているため、コンパイルエラーとなる。
⑦equalsメソッドはオーバーライドされないと同値性ではなく、同一性を検証する(==演算子と同じ)する。
array2には、array1のcloneメソッドの結果作られた新しい配列への参照が入っている。そのため、array1とarray2は異なる参照を持っていることから、equalsメソッドはfalseを戻す。
読解力のない自分用への解説
正直この解説が図解もなく全然意味が分からなかったので、以下のように自分用に解説。
全体イメージ図
Cloneメソッドで何が起きているか
①まずメモリ内の8221番地にある既存の配列から、8567番地に同じ要素を持つ配列を新規に作成する。
②変数Array2には、8567番地が代入される。(Array1には、8221番地が代入されている。)
変数同士の比較結果
Array1とArray2はそれぞれ違う番地の値が代入されている。
==演算子による比較と、オーバーライドされていないequalsメソッドは、この番地が同じかどうかを検証するので、
結果はfalse
が返される。
dokojavaを使用して実践。
要素同士の比較結果
Array1[1] と Array2[1]を比較した場合、Cloneメソッドでは配列内の要素が同じ状態で作成される。
なので、各Indexが参照している参照先(番地)は同じになる。
よってArray1の1番目の配列と、Array2の1番目の配列は、配列の実体自体は違う番地にあるが、各要素が指している次の配列の参照先は同じということになる。
よって要素同士の検証すると、
結果はtrue
が返される。
dokojavaを使用して実践。
一次元配列の場合
一次元配列の場合も同様。
変数名同士を比較することは、配列の番地を比較することになる。
よって、Array1[1] == Array3 もfalseが返される。
しかし、配列の先の要素は同じものを参照しているので、同じ値でtrueが返される。
注意点
全体イメージ図とここまでの説明を振り返れば当然のことだが、既存の配列とCloneしてできた配列は、要素は同じだが それぞれ独立した配列である。
よって格納している要素にもよるが、片方の配列の要素を変更しても、もう一方が影響を受けるわけではない。