Javaのcloneメソッドとシャローコピー

## はじめに
新卒1年目の時、Java Silverの問題集に掲載されていたある問題にかなり悩まされた。その際Javaの仕様とコンピュータのメモリについて勉強できたので、自分なりに記しておく。

# 目次
問題設定
前提知識
コピーの方法
解答
感想


# 問題設定
**問題：A~Dのコードを実行したとき、`True`になるのはどれか**
```定義.java
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)`

`clone`メソッドの詳細がよくわからなくても、Bは次元が異なるので不正解（`False`になる）であることがわかる。残りの3つについて以下で見ていく。

# 前提知識
### 同一性と同値性
プログラミングを勉強し始めるとすぐにこの概念に辿り着く。
- **同値性**：変数の中の値が同じかどうか
- **同一性**：変数の参照先（アドレス）が同じかどうか

これらはよく`==`と`equals`メソッドの問題として出題されるので、仕様も含めてサンプルコードで確認する。
```java
String str1 = "aaa";
String str2 = new String("aaa");
System.out.println(str1 == str2);   //false
System.out.println(str1.equals(str2));   //true
        
String[] arr1 = {"bbb"};
String[] arr2 = new String[]{"bbb"};
String[] arr3 = arr1;
System.out.println(arr1 == arr2);   //false
System.out.println(arr1.equals(arr2));   //false
System.out.println(arr1 == arr3);   //true
System.out.println(arr1.equals(arr3));   //true
```
- `str1 == str2`は同一性をチェックする。`str1`と`str2`はインスタンスが異なる(メモリ上のアドレスが異なる)ため`False`が返る。
- `str1.equals(str2)`は同値性をチェックする。`str1`と`str2`のメモリ上のアドレスは異なるが、文字列は同じであるため`True`が返る。

単純な文字列の例は簡単だが、これが配列になると少しややこしい。

- `arr1 == arr2`も同一性をチェックする。`str1`と`str2`はインスタンスが異なるため`False`が返る。
- `arr1.equals(arr2)`の`equals`は**同一性**をチェックする。これは配列に用いる`equals`メソッドが`Object`クラスのメソッドであり、それは同一性をチェックするからだ。よって`arr1`と`arr2`はインスタンスが異なるため`False`を返す。
- `arr1 == arr3`はもちろん`True`
- `arr1.equals(arr3)`は上で見た通り**同一性**をチェックする。`arr1`と`arr3`は同じインスタンスだから`True`を返す。

これは二重配列になっても同じである。
```java
String[][] arr4 = {{"a"}, {"b"}};
String[][] arr5 = new String[][]{{"a"}, {"b"}};
System.out.println(arr4 == arr5);   //false
System.out.println(arr4.equals(arr5));   //false
```

### cloneメソッド
その名の通り、自分自身をコピーする。何をやっているかは下のコードを見ていただきたい。
```java
String[][] arr6 = arr4.clone();
for(int i=0;i<2;i++){
    System.out.println("arr6の" + i + "0成分は" + arr6[i][0]);
}
```
上で定義した二重配列`arr4`のcloneを`arr6`に代入した。コピーであればfor文も実行できるはずである。実際このコードは正しく処理され、想定通りの結果が返ってくる。
```
arr6の00成分はa
arr6の10成分はb
```
さて、今度はこれらの同一性や同値性を考えてみよう。
```java
System.out.println(arr4 == arr6);
System.out.println(arr4.equals(arr6));
System.out.println(arr4[0] == arr6[0]);
System.out.println(arr4[0].equals(arr6[0]));
```
```
false
false
true
true
```
この結果には当時かなり違和感を持った。`arr4 == arr6`という"全体"は同一でないのに、`arr4[0] == arr6[0]`という"部分"は同一といっているからだ。
この疑問を解消するためにはコピーのされ方ついて知っておく必要がある。



# コピーの方法
ここで、コピーには**シャローコピー**と**ディープコピー**の２種類があることをおさえておきたい。特に、Javaの`clone`メソッドは内部でシャローコピーを行っている。
- **シャローコピー**
    メモリ上にある実体はコピーせず、アドレスだけコピーしている。
- **ディープコピー**
    メモリ上にある実体とそのアドレスを再帰的にコピーする。
 

`arr4`と`arr6`を使ってシャローコピーの性質をみてみると次のようになる。
```java
System.out.println(arr4 == arr6);   //false
System.out.println(arr4[0] == arr6[0]);   //true
```
通常はコピーを行うとメモリ上の別のアドレスに同じ実体が作成される。すると、`==`で同一性をチェックすると`False`となりそうだ。しかし、`arr4[0] == arr6[0]`はそうではない。これは`arr4`のアドレスのみがメモリ上でコピーされ、`arr4[0]`の実体はコピーされていないからだ。なお、`arr4[0]`と`arr6[0]`は同じ実体を参照しているため、どちらかに変更を加えるともう一方にも影響が出る。





# 解答
ここまでを踏まえて最初の問題に解答してみる。
- **A**：`array1[1] == array3`
    - `array1[1] == array1[1].clone()`ということであるが、`clone`元とそのコピーはアドレスが異なるため不正解。
- **B**：`array1[1] == array3[1]`
    - 次元が異なるので不正解。
- **C**：`array1[1] == array2[1]`
    - `array1[1] == array1.clone()[1]`であり、同じアドレスを指しているため**正解**となる。
- **D**：`array1.equals(array2)`
    - 上で述べた通り、配列の`equals`は同一性をチェックするため不正解。

というわけで正解は**C**だった。試験としてはJavaの仕様を理解していれば合格できるが、この問題はそこから少しだけはみ出た内容となっている。

# 感想
頭の中でメモリをイメージして、コピーの挙動を理解できれば解きやすくなると思う。この記事を書くために過去に勉強した内容を振り返っていたが、Javaの仕様だけでなくその他のコンピュータの基本部分にも興味が湧いた。