ArrayListの罠
このレッスンを終えると、ArrayList の remove がどちらの意味で呼ばれるのかを、引数の型から判断できるようになります。
まず予測してください
次のコードは何を表示するでしょうか。
List<Integer> list = new ArrayList<Integer>();
list.add(10);
list.add(20);
list.add(30);
list.remove(1);
System.out.println(list);答えは [10, 30] です。消えたのは値の 1 ではありません。添字の 1 番目、つまり 20 です。
remove は2つある
List には remove(int index) と remove(Object o) の2つがあります。名前が同じでオーバーロードされているので、引数の型で呼ばれる相手が決まります。
渡したものが int なら添字の方が選ばれます。値で消したいときは list.remove(Integer.valueOf(30)) のようにラッパー型にして渡すか、list.remove((Integer) obj) とキャストします。オートボクシングは「他に合う候補が無いとき」の最後の手段なので、int を渡した時点で添字版が確実に勝ちます。ここは Silver の定番中の定番です。
List の要素が Integer のときだけ起きる罠であることも押さえてください。要素が String なら remove("a") は値の削除になります。
添字がずれる
削除すると、後ろの要素が1つずつ前に詰まります。前から順に添字を進めながら消していくと、消したつもりの隣を飛ばしてしまいます。for (int i = 0; i < list.size(); i++) の中で remove するコードは、この詰まりのせいで期待どおりに動きません。試験では出力を答えさせる形で出ます。
また、範囲外の添字を渡すと IndexOutOfBoundsException が実行時に飛びます。remove(Object) の方は、見つからなければ何も起きず false を返すだけです。同じ名前でも例外の出方まで違う点に注意してください。
戻り値も違う
remove(int) は消した要素を返し、remove(Object) は消せたかどうかの boolean を返します。戻り値を使う問題では、ここでも型が食い違ってコンパイルエラーになる形が作れます。
要件
- 10, 20, 30, 40 をこの順に add する
- 引数 target を添字として1件削除する
- 続けて値 30 を削除する。ただし 30 がすでに無ければ何も起きなくてよい
- 残った List の toString の結果と size を、スラッシュでつないで返す
入出力例
shrink(0) → "[20, 40]/2"
shrink(1) → "[10, 40]/2"
shrink(2) → "[10, 20, 40]/3"
shrink(3) → "[10, 20]/2"