インデックス付き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;
	}
}