JavaでListを使う上で、
「他の言語でいうところのforeach文はないの?」
「Listのfor文を書く時に末尾(逆順)から処理を行いたいけど方法がわからない」
「Listのループ処理の書き方は一種類じゃないの?」
「iteratorはどうやって使うの?」
といったお声を耳にします。
そういった方は必見です!
この記事を見ればListを使った繰り返し処理をわかりやすく学べます!
なお、Javaの記事については、こちらにまとめています。
getメソッドを使った場合のループ処理
使い方
まずは、配列に近い使い方を見てみましょう。
List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(4); for(int i = 0; i < list.size(); i++) { Integer value = list.get(i); }
getメソッドを使うことで配列の要素をひとつひとつ取得することが可能です。
もし引数に要素数を超える値を指定した場合はjava.lang.IndexOutOfBoundsException例外が発生します。
注意
上記のコードではfor文を繰り返す際に毎回sizeメソッドが呼ばれ要素数を取得しています。
要素数が数千、数万といったListの場合には毎回このメソッドが呼ばれるのは少々問題がありそうです。
そのため次のように改善したいと思います。
int size = list.size(); for(int i = 0; i < size; i++) { Integer value = list.get(i); }
変数sizeを定義しそこにListのサイズをキャッシュしておくことで毎回処理が呼ばれるのを防ぎます。
こうすることで無駄な呼び出しを防ぐことができますが別の問題が発生します。
Listの要素を削除をする場合を見てみましょう。
int size = list.size(); for(int i = 0; i < size; i++) { Integer value = list.get(i); list.remove(i); }
removeメソッドを使って要素を削除していますがこのコードではgetメソッドを呼ぶことで最終的には例外が発生してしまいます。
なぜかというとlistのサイズが変更されたにも関わらず変数sizeの値は変わっていないため実際の要素数を超える場所にアクセスしようとして例外が発生してしまいます。
そのため次のように
int size = list.size(); for(int i = 0; i < size; i++) { Integer value = list.get(i); list.remove(i); size = list.size(); }
とする必要が出てきますがこれでは結局意味がありません。
getメソッドを使う方法ではjava.lang.IndexOutOfBoundsException例外に常に気をつけなければなりません。
ではjava.lang.IndexOutOfBoundsException例外を気にしないでループ処理を行うにはどうすればいいでしょうか。
次の項目で解説します。
iteratorを使った場合のループ処理
iteratorとは
iteratorは配列の要素番号(index)や要素数を気にせずループ処理を行うための機能です。
iteratorを使うことで削除などといった各要素に対して要素番号を直接指定する必要がありません。
使い方
では早速使い方を見てみましょう。
先頭から処理を行う
List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(4); Iterator it = list.iterator(); while(it.hasNext()) { Integer value = (Integer)it.next(); }
getメソッドの解説で一番最初のコードをiteratorを使った場合このような形にすることができます。
hasNextメソッドでは次に要素がある間はループ処理を繰り返します。
そしてnextメソッドを呼び出すとイテレータを次の要素に進めると同時に次の要素を返します。
上記のコードではその値をIntegerに型キャスト(変換)して変数valueに代入しています。
一回目の場合は1が入ります。
次に先程問題になった削除をする場合はどうすればよいでしょうか。
List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(4); Iterator it = list.iterator(); while(it.hasNext()) { Integer value = (Integer)it.next(); it.remove(); }
イテレータにもremoveメソッドがあり、そのメソッドを呼び出すことで要素の位置を直接指定することなく要素を削除することができます。
末尾から処理を行う
先程は先頭からイテレータを進めていく要素を見ていきましたが今度は末尾から先頭に向かって進んでいく場合はどうすればいいかを見ていきます。
List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(4); ListIterator it = list.listIterator(list.size()); while(it.hasPrevious()) { Integer value = (Integer)it.previous(); }
前項とは少し違いListIteratorというものを使って繰り返し処理を行います。
listIteratorメソッドを呼ぶことで指定した位置のイテレータを取得することが可能です。
今回の場合は末尾から処理を行いたいためlist.sizeで要素数を取得後、引数として与えています。
次にwhile文ですがこちらも先程とは変わっています。
hasPreviousメソッドを使い、一つ前の要素が存在している間はループ処理をするという書き方をしています。
ループ中についても変化があります。
先程のコードではit.nextとしていたかと思いますが今回はit.previousという記述になっています。previousメソッドを使うことで一つ前の要素を取得すると同時に要素を一つ前に戻すことができます。
拡張for文(他言語のforeachにあたるもの)
拡張for文とは
他のプログラム言語ではforeachという機能が備わっていることがあります。
foreachとは先程解説したiteratorよりもシンプルに扱うことが可能です。
しかしJavaではforeachと記述するとそのような命令はないと怒られてしまいます。
ではどのようにすればforeachのような処理が実現できるでしょうか。
使い方
List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(4); for(Integer value : list) { System.out.printf("%d", value); }
for文を使ってforeachのような処理を実現することができます。
これを拡張for文と呼びます。
使い方の説明ですがfor文中のコロン右側にはループ処理を行いたいListを指定し左側にはひとつずつ取得した要素の値を代入しています。
for文の使い方総まとめ
for文についてさらに詳しく知りたいときはこちらの記事を確認してください!
拡張for文とJava8のforEachの使い方総まとめ
拡張for文とJava8のforEachの使い方についてさらに詳しく知りたいときはこちらの記事を確認してください!
まとめ
要素番号を指定して値を取得したい場合はgetメソッドを使うことで値を取得することが可能です。
要素番号を指定しないでListの繰り返し処理をする方法としてiteratorを使った方法や拡張for文を使う方法があることを学びました。
Listの繰り返し処理でもし使い方を忘れてしまったらぜひこの記事を忘れたら思い出して下さい!