Javaの多次元配列がめんどくさい話

※結論だけ知りたい人はサンプルコードを見てください

 

その日、私はAtCoderで、

D: 画像処理高橋君 - AtCoder Beginner Contest 039 | AtCoder

を解いていました。

 

解法は Editorial にある通りなのでそちらを参照していただくとして、これを自力で思いついた私は意気揚々と実装に入ったわけです。

まず入力から配列を生成し、コピーし、白い部分を広げて、黒い部分を広げて、元の配列と一致していればセーフ。

(「白い部分を広げる」「黒い部分を広げる」はほぼ同じ操作なので、1つのメソッドで書けますね。)

 

…サンプルが通りません。というわけで生成された配列を出力してみると、コピーの時点でおかしいことが発覚。

 

cloneを使って配列を複製しようとしていたのですが、cloneの定義はこんな感じ。

Object (Java Platform SE 8)

「絶対的な要件ではありません」ばっかりなのが腑に落ちないのですが、とりあえず配列に対して適用すると、新しい配列オブジェクトを作り、中身が全部=で代入されると思えば間違いではないようです。

で、今回使っていたのはboolean型の2次元配列。プリミティブ型だから深く考えなくても大丈夫、と思っていたのですが、深く考えなくてもJavaの多次元配列は配列の配列なわけで、つまり配列としてa[0]==b[0]が成立してしまうということになります。こういうのを「シャローコピー」とかいうらしいですね。

仕方がないので二重ループでせこせこコピー。

 

…やっぱりダメです。出力してみると、比較するべきものは正しくできています。ということは比較がダメ。配列のequalsは==と同じようです。どないやねん、ということでちょっとググり、Arrays.equalsで比較。でもダメ。仕方がないので二重ループでせこせこ比較。

 

…していたのですが、今調べたらArrays.deepEqualsというメソッドがあるじゃないですかー。やだー。でもどうせならdeepCloneも作ってほしかったなー、なんて。

 

サンプルコードと実行結果を以下にまとめておきます。

コード

import java.util.Arrays;

public class TestClass {

public static void main(String args) {
// 二次元配列
int
a ={{0,0},{0,0}};
int
b =a.clone();

System.out.println(Arrays.deepToString(a));
System.out.println(Arrays.deepToString(b));

//シャローコピーなのを確認
System.out.println(a[0]==b[0]);

//aだけ変更(のつもり)
a[0][0]=1;

//bも変わってる
System.out.println(Arrays.deepToString(a));
System.out.println(Arrays.deepToString(b));

int
[] c={{1,0},{0,0}};

//これはダメ
System.out.println(a.equals(c));
//これもダメ
System.out.println(Arrays.equals(a, c));
//これはOK
System.out.println(Arrays.equals(a[0],c[0]));
//これもOK
System.out.println(Arrays.deepEquals(a, c));

}

}

実行結果

[[0, 0], [0, 0]]
[[0, 0], [0, 0]]
true
[[1, 0], [0, 0]]
[[1, 0], [0, 0]]
false
false
true
true