- ベストアンサー
JAVAでの、clone() 参照渡し?について
- ArrayListで文字列の配列(String[ ])の配列を作りました。
- list.add(item)だと上手くいかず、list.add(item.clone())で上手くいく理由を知りたいです。
- item.clone()は参照渡しを行うため、異なる値が保存されます。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
「参照」について、より詳しく勉強するのがよいでしょう。 また、「浅いコピー」「深いコピー」の話も読んでみましょう 理解していないと、今後も悩まされることになります String[] item = new String[6]; これで、itemにあるString[6] への参照値が代入されました。 この値を 500 としましょう。 tem[0] = rs.getString("Field"); item[1] = rs.getString("Type"); item[2] = rs.getString("Null"); item[3] = rs.getString("Key"); item[4] = rs.getString("Default"); item[5] = rs.getString("Extra"); これらの item[i]は「変数itemに納められている『参照値500』のところにあるString[]のi番目の要素」です。 変数itermの「参照値500」が変化するわけではありません。 list.add(item); ここで、listにaddされるのは「参照値500」です。その内容ではありません。 つまりlistは 参照値500,参照値500,参照値500,参照値500,... となります。 list(i)が同じ参照なのですから、そのj番目も同じになります list.add(item.clone()); cloneにより、同じ内容の新しいオブジェクトができます。新しいオブジェクトなので、参照も既存のもととは別になります。 よって、listは 参照値510,参照値511,参照値512,参照値513,... 等と、全てが別の参照となり、別々の値を保存できます。 // 例として連番にしましたが、実際にはそのような続き番号になる保証はありません // 参照値そのものを意識することは通常ありません whileの内側に String[] item = new String[6]; を入れる、というのも、毎回新しいString[6]を作ってlistに追加する、ということで、上記cloneと同様のlistができます。
その他の回答 (4)
- Ogre7077
- ベストアンサー率65% (170/258)
Java では new しない限り、新しいオブジェクト(を格納したメモリ領域)は生成されません。 ご提示のプログラムの場合なら 1個目: new ArrayList<String[]>() 2個目: new String[6] の二つのみです (rs.getString が内部的に new String() を行っている可能性は無視します) オブジェクトの生成は大変重い処理とされています。 ゆえに引数や変数でオブジェクトを扱う場合は オブジェクトを一意に識別できる番号だけを受け渡して、 メモリ領域には直接触れないでやり取りします。 つまり「参照渡し」と呼ばれる処理形態ですね。 list.add(item) も変数代入と同じ処理なので、 処理完了時の1個目のオブジェクトの中身は 0: 2個目のオブジェクト(を一意に識別できる番号) 1: 2個目のオブジェクト(を一意に識別できる番号) 2: 2個目のオブジェクト(を一意に識別できる番号) 3: 2個目のオブジェクト(を一意に識別できる番号) ... となり、まったく同じオブジェト(を一意に識別できる番号)が 複数個入ったリストになります。 list.add(item.clone()) ならば、clone の内部で new された オブジェクト(を一意に識別できる番号)を追加するので、 0: 3個目のオブジェクト(を一意に識別できる番号) // ループ1回目の2個目のオブジェクトと同値 1: 4個目のオブジェクト(を一意に識別できる番号) // ループ2回目の2個目のオブジェクトと同値 2: 5個目のオブジェクト(を一意に識別できる番号) // ループ3回目の2個目のオブジェクトと同値 3: 6個目のオブジェクト(を一意に識別できる番号) // ループ4回目の2個目のオブジェクトと同値 ... となります。
- teketon
- ベストアンサー率65% (141/215)
A.String型配列への参照を保持する変数item B.String型の参照を保持する配列の変数item[0]~item[5] でitemは構成されています。 list.add(item)の場合、 上記Bを書き換えていますが、上記Aの書き換えは行っていません。 そのため、すべてのlistは最後の要素と同じ内容になります。 例 1周め.list(0)[0]=A,list(0)[1]=B,list(0)[2]=C 2周め.list(0)[0]=D,list(0)[1]=E,list(0)[2]=F,list(1)[0]=D,list(1)[1]=E,list(1)[2]=F 3週め.list(0)[0]=G,list(0)[1]=H,list(0)[2]=I,list(1)[0]=G,list(1)[1]=H,list(1)[2]=I,list(2)[0]=G,list(2)[1]=H,list(2)[2]=I list.add(item.clone())の場合、 配列をclone()すると上記Aをもとに、新しいString型配列への参照を作成します(つまり新しいAを作っている)。 そのため、すべてのlistは別の値を保持します。 例 1周め.list(0)[0]=A,list(0)[1]=B,list(0)[2]=C 2周め.list(0)[0]=A,list(0)[1]=B,list(0)[2]=C,list(1)[0]=D,list(1)[1]=E,list(1)[2]=F 3週め.list(0)[0]=A,list(0)[1]=B,list(0)[2]=C,list(1)[0]=D,list(1)[1]=E,list(1)[2]=F,list(2)[0]=G,list(2)[1]=H,list(2)[2]=I これらの動作は誤解を招きやすいので、下記のようにすることをオススメします。 ArrayList<String[]> list = new ArrayList<String[]>(); //このwhileは数回繰り返されますが、下記の値は毎度異なります。 while(rs.next()) { //ループの中で配列を作成する String[] item = new String[6]; item[0] = rs.getString("Field"); item[1] = rs.getString("Type"); item[2] = rs.getString("Null"); item[3] = rs.getString("Key"); item[4] = rs.getString("Default"); item[5] = rs.getString("Extra"); list.add(item); }
- wormhole
- ベストアンサー率28% (1626/5665)
>listにaddするときには、itemの値は毎回上書きされていて、 >つまり毎回異なるitemを渡しているはずだと思うのですが、 どこでitemが上書きされてるのか教えてください。 少なくとも質問に書かれてるコード上にはそのようなヶ所はありません。
- wormhole
- ベストアンサー率28% (1626/5665)
itemのインスタンスそのものはずっと同じになってますが(new String[6]は1回しかされない)。 ArrayList<String[]> list = new ArrayList<String[]>(); while (rs.next()) { String[] item = new String[6]; item[0] = ... ... list.add(item); } 違いわかりますか?
お礼
回答ありがとうございます。 String[] item = new String[6]; をwhileの外側で作っても、 listにaddするときには、itemの値は毎回上書きされていて、 つまり毎回異なるitemを渡しているはずだと思うのですが、 外側で作ると、どんな不都合がおこるのでしょう。