JavaでのIterableとIteratorの使い方の詳細
5684 ワード
Javaでは、リストセットを次のように巡回できます.
1つ目は普通のforサイクルで、2つ目は反復器遍歴で、3つ目はfor eachサイクルです.
Javaのiteratorオブジェクトとiterableオブジェクトの2つの方法について説明します.次に、この2つのオブジェクトの違いと、カスタムクラスでfor eachループを実現する方法を見てみましょう.
IteratorとIterable
iteratorはJavaにおける反復器オブジェクトであり,Listのような集合を反復的に遍歴できる下位依存である.一方、iterableインタフェースでは、iteratorを返す方法が定義されており、iterableインタフェースのクラスがfor eachループをサポートすることができるように、iterableインタフェースをカプセル化することに相当します.
iterator内部詳細
jdkのIteratorインタフェースの主な方法は以下の通りです.
iteratorは以上の2つの方法によって集合への反復アクセスの方法を定義し,具体的な実装方式は異なる実装クラスに依存し,具体的な集合クラスはIteratorインタフェースの方法を実装して反復を実現する.
リストにはIteratorインタフェースが実装されていないが,実装されたIterableインタフェースが実装されていることがわかる.さらにIterableインタフェースのソースコードを観察すると、Iteratorオブジェクトが返されただけであることがわかります.
したがって、リストを反復するには、(iterator()メソッドを呼び出すことによって)次の方法を用いることができる.
同時にIterableインタフェースを実現し、for eachループを使用することもできます.
for each原理
実はfor eachサイクルの内部もIterator反復器に依存していますが、Javaが提供する文法糖にすぎず、JavaコンパイラはそれをIterator反復器に変換して遍歴します.次のfor eachサイクルを逆コンパイルします.
逆コンパイル後:
Javaのfor each増強サイクルはiterator反復器方式で実現されていることがわかります.
IterableとIteratorの関係を深く検討する
なぜhasNext(),next()メソッドをIterableインタフェースに直接置かず,他のクラスが直接実装すればよいのかという問題がある.
なぜなら、一部の集合クラスは、LinkedListのListItrとDescendingIteratorの2つの内部クラスのような複数のIterator内部クラスを実装することができ、それぞれ双方向遍歴と逆シーケンス遍歴を実現する遍歴方式ではない可能性があるからである.異なるIteratorを返すことで、異なる遍歴方式を実現することで、より柔軟になります.2つのインタフェースを統合すると、異なるIterator実装クラスに戻ることはできません.ListItr関連ソースコードは以下の通りである.
上記のようにlist.listIterator()メソッドを呼び出すことでiterator反復器を返すことができます(list.iterator()はデフォルトの実装にすぎません)
DescendingIteratorのソースコードは次のとおりです.
この反復器はlist.descendingIterator()でも使用できます.
自分の反復器を実現する
カスタムクラスArrayMapがあり、次のようにfor eachを巡回します.
hashNextとnext抽象法は実装されていないため,それを遍歴することはできない.
カスタム反復クラス
まず,反復器クラス実装hashNextとnextメソッドをカスタマイズし,ArrayMapの内部クラスとして用い,関連コードは以下の通りである.
nextで指定した遍歴規則はArrayMapのkey値に基づいて遍歴していることがわかります.上記の反復器クラスがあれば、iterator方式を使用して外部で遍歴することができます.遍歴コードは以下の通りです.
上記のように、外部クラスが内部クラスオブジェクトを作成する方法に注意して、KeyIteratorオブジェクトを作成することで反復アクセスを行います.
for eachサイクルをサポート
まだiterableインタフェースを実装していないため、for eachループアクセスはサポートされていません.まずArrayMapでIterableインタフェースを実装します.
次にiterator()メソッドを書き換え、独自の反復オブジェクト(iterator)を返します.
カスタムのKeyIteratorクラスでは、iterator()メソッドで返されるタイプが一致しない場合は、Iteratorインタフェースを実装する必要があります.
List list = new ArrayList<>();
list.add(5);
list.add(23);
list.add(42);
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.print(it.next() + ",");
}
for (Integer i : list) {
System.out.print(i + ",");
}
1つ目は普通のforサイクルで、2つ目は反復器遍歴で、3つ目はfor eachサイクルです.
Javaのiteratorオブジェクトとiterableオブジェクトの2つの方法について説明します.次に、この2つのオブジェクトの違いと、カスタムクラスでfor eachループを実現する方法を見てみましょう.
IteratorとIterable
iteratorはJavaにおける反復器オブジェクトであり,Listのような集合を反復的に遍歴できる下位依存である.一方、iterableインタフェースでは、iteratorを返す方法が定義されており、iterableインタフェースのクラスがfor eachループをサポートすることができるように、iterableインタフェースをカプセル化することに相当します.
iterator内部詳細
jdkのIteratorインタフェースの主な方法は以下の通りです.
public interface Iterator {
boolean hasNext();
E next();
}
iteratorは以上の2つの方法によって集合への反復アクセスの方法を定義し,具体的な実装方式は異なる実装クラスに依存し,具体的な集合クラスはIteratorインタフェースの方法を実装して反復を実現する.
リストにはIteratorインタフェースが実装されていないが,実装されたIterableインタフェースが実装されていることがわかる.さらにIterableインタフェースのソースコードを観察すると、Iteratorオブジェクトが返されただけであることがわかります.
public interface Iterable {
Iterator iterator();
}
したがって、リストを反復するには、(iterator()メソッドを呼び出すことによって)次の方法を用いることができる.
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.print(it.next() + ",");
}
同時にIterableインタフェースを実現し、for eachループを使用することもできます.
for each原理
実はfor eachサイクルの内部もIterator反復器に依存していますが、Javaが提供する文法糖にすぎず、JavaコンパイラはそれをIterator反復器に変換して遍歴します.次のfor eachサイクルを逆コンパイルします.
for (Integer i : list) {
System.out.println(i);
}
逆コンパイル後:
Integer i;
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
i = (Integer)iterator.next();
}
Javaのfor each増強サイクルはiterator反復器方式で実現されていることがわかります.
IterableとIteratorの関係を深く検討する
なぜhasNext(),next()メソッドをIterableインタフェースに直接置かず,他のクラスが直接実装すればよいのかという問題がある.
なぜなら、一部の集合クラスは、LinkedListのListItrとDescendingIteratorの2つの内部クラスのような複数のIterator内部クラスを実装することができ、それぞれ双方向遍歴と逆シーケンス遍歴を実現する遍歴方式ではない可能性があるからである.異なるIteratorを返すことで、異なる遍歴方式を実現することで、より柔軟になります.2つのインタフェースを統合すると、異なるIterator実装クラスに戻ることはできません.ListItr関連ソースコードは以下の通りである.
public ListIterator listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
private class ListItr implements ListIterator {
...
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
public boolean hasNext() {
return nextIndex < size;
}
...
上記のようにlist.listIterator()メソッドを呼び出すことでiterator反復器を返すことができます(list.iterator()はデフォルトの実装にすぎません)
DescendingIteratorのソースコードは次のとおりです.
public Iterator descendingIterator() {
return new DescendingIterator();
}
private class DescendingIterator implements Iterator {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
この反復器はlist.descendingIterator()でも使用できます.
自分の反復器を実現する
カスタムクラスArrayMapがあり、次のようにfor eachを巡回します.
ArrayMap am = new ArrayMap<>();
am.put("hello", 5);
am.put("syrups", 10);
for (String s: am) {
System.out.println(s);
}
hashNextとnext抽象法は実装されていないため,それを遍歴することはできない.
カスタム反復クラス
まず,反復器クラス実装hashNextとnextメソッドをカスタマイズし,ArrayMapの内部クラスとして用い,関連コードは以下の通りである.
public class KeyIterator implements Iterator {
private int ptr;
public KeyIterator() {
ptr = 0;
}
@Override
public boolean hasNext() {
return (ptr != size);
}
@Override
public K next() {
K returnItem = keys[ptr];
ptr += 1;
return returnItem;
}
}
nextで指定した遍歴規則はArrayMapのkey値に基づいて遍歴していることがわかります.上記の反復器クラスがあれば、iterator方式を使用して外部で遍歴することができます.遍歴コードは以下の通りです.
ArrayMap am = new ArrayMap<>();
am.put("hello", 5);
am.put("syrups", 10);
ArrayMap.KeyIterator ami = am.new KeyIterator();
while (ami.hasNext()) {
System.out.println(ami.next());
}
上記のように、外部クラスが内部クラスオブジェクトを作成する方法に注意して、KeyIteratorオブジェクトを作成することで反復アクセスを行います.
for eachサイクルをサポート
まだiterableインタフェースを実装していないため、for eachループアクセスはサポートされていません.まずArrayMapでIterableインタフェースを実装します.
public class ArrayMap implements Iterable {
private K[] keys;
private V[] values;
int size;
public ArrayMap() {
keys = (K[]) new Object[100];
values = (V[]) new Object[100];
size = 0;
}
....
}
次にiterator()メソッドを書き換え、独自の反復オブジェクト(iterator)を返します.
@Override
public Iterator iterator() {
return new KeyIterator();
}
カスタムのKeyIteratorクラスでは、iterator()メソッドで返されるタイプが一致しない場合は、Iteratorインタフェースを実装する必要があります.