インデックス付きforeachループ
(1/8改良版を末尾に追加)
問題
foreachループでインデックスが使いたい。(今何番目?)
かといって下のようにローカル変数iを使うとiがループの後で見えてしまうのが嫌。
参考:foreachでインデックスが使いたい場合はそれなりにある
http://blog.livedoor.jp/thinkmeta/archives/51306169.html
//前略 List<Integer> list = new ArrayList<Integer>(); //中略 int i = 0; for (Integer integer : list) { //integerでなんかする i++; } //以下iが見える
対策
Iterableをラップして、
インデックスと要素のペアを取り出せるイテレータをもつ
別のIterableを作る。
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class IndexIterable<T> implements Iterable<IndexElement<T>> { public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); list.add(100); list.add(200); list.add(300); //インデックス付きforeach for(IndexElement<Integer> ie : withIndex(list)){ System.out.println(ie.getElement() + " @ " + ie.getIndex() + "ばんめ"); } } //Iterableをラップ private Iterable<T> iterable; public IndexIterable(Iterable<T> iterable) { this.iterable = iterable; } //newを書きたくないのでstaticメソッドでインスタンス作成 public static <T> Iterable<IndexElement<T>> withIndex(Iterable<T> iterable){ return new IndexIterable<T>(iterable); } @Override public Iterator<IndexElement<T>> iterator() { return new IndexedIterator(iterable.iterator()); } public class IndexedIterator implements Iterator<IndexElement<T>>{ private Iterator<T> elementIterator; private int idx = 0; public IndexedIterator(Iterator<T> elementIterator) { this.elementIterator = elementIterator; } @Override public boolean hasNext() { return elementIterator.hasNext(); } @Override public IndexElement<T> next() { //next()で例外が出てもインデックスがずれないように注意 T elem = elementIterator.next(); return new IndexElement<T>(idx++, elem); } @Override public void remove() { elementIterator.remove(); } } } class IndexElement<T> { private int index; private T element; public IndexElement(int index, T element) { super(); this.element = element; this.index = index; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public T getElement() { return element; } public void setElement(T element) { this.element = element; } }
実行結果
100 @ 0ばんめ 200 @ 1ばんめ 300 @ 2ばんめ
使用感
- 型変数が必要(ここではInteger)
- getIndexとgetElementが長い
そのためか、あんまり書きやすくなった気がしない。
これがあまり見たことのない理由なのか。
ちなみに、コレクションの生成がめんどくさいという問題
(上記プログラムではリスト生成に4行使っている)
には可変長引数で対処するのがいいらしい。
http://d.hatena.ne.jp/bleis-tift/20090906/1252237779
Scalaの標準ライブラリではよく見かける。
(1/8改良版)
- メソッド名簡略化
- IEインターフェースに抽象化
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class IndexIterable2<T> implements Iterable<IE<T>> { public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); list.add(100); list.add(200); list.add(300); for(IE<Integer> ie : withIndex(list)){ System.out.println(ie.elem() + " @ " + ie.idx() + "ばんめ"); } } //Iterableをラップ private Iterable<T> iterable; public IndexIterable2(Iterable<T> iterable) { this.iterable = iterable; } //newを書きたくないのでstaticメソッドでインスタンス作成 public static <T> Iterable<IE<T>> withIndex(Iterable<T> iterable){ return new IndexIterable2<T>(iterable); } @Override public Iterator<IE<T>> iterator() { return new IndexedIterator(iterable.iterator()); } public class IndexedIterator implements Iterator<IE<T>>{ private Iterator<T> elementIterator; private int idx = 0; public IndexedIterator(Iterator<T> elementIterator) { this.elementIterator = elementIterator; } @Override public boolean hasNext() { return elementIterator.hasNext(); } @Override public IE<T> next() { //next()で例外が出てもインデックスがずれないように注意 T elem = elementIterator.next(); return new IndexElement<T>(idx++, elem); } @Override public void remove() { elementIterator.remove(); } } } //(インデックス, 要素)のインターフェース interface IE<T> { public int idx(); public T elem(); } class IndexElement<T> implements IE<T>{ private int index; private T element; @Override public int idx() { return getIndex(); } @Override public T elem() { return getElement(); } public IndexElement() { } public IndexElement(int index, T element) { super(); this.element = element; this.index = index; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public T getElement() { return element; } public void setElement(T element) { this.element = element; } }