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 を返します。戻り値を使う問題では、ここでも型が食い違ってコンパイルエラーになる形が作れます。

要件

  1. 10, 20, 30, 40 をこの順に add する
  2. 引数 target を添字として1件削除する
  3. 続けて値 30 を削除する。ただし 30 がすでに無ければ何も起きなくてよい
  4. 残った List の toString の結果と size を、スラッシュでつないで返す

入出力例

shrink(0)"[20, 40]/2" shrink(1)"[10, 40]/2" shrink(2)"[10, 20, 40]/3" shrink(3)"[10, 20]/2"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.java
学習モード

メモ

ArrayListの罠

⌘S で保存