web-dev-qa-db-ja.com

イテレータとfor

インタビューで、forループを使用してイテレーターを使用する利点は何ですか、またはイテレーターよりもforループを使用する利点は何ですか?

将来的に同様の質問に直面した場合、私はそれに答えることができるように、誰でもこれに答えてください

64
rocking

まず第一に、2種類のforループがあり、それらの動作は大きく異なります。インデックスを使用します:

_for (int i = 0; i < list.size(); i++) {
    Thing t = list.get(i);
    ...
}
_

この種のループは常に可能とは限りません。たとえば、リストにはインデックスがありますが、セットには順序がありませんので、セットにはインデックスがありません。

もう1つのforeachループは、舞台裏でイテレーターを使用します。

_for (Thing thing : list) {
    ...
}
_

これは、あらゆる種類のIterableコレクション(または配列)で機能します。

最後に、イテレータを使用できます。イテレータは、イテラブルでも機能します。

_for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    Thing t = it.next();
    ...
} 
_

したがって、実際には3つのループを比較できます。

パフォーマンス、可読性、エラー傾向、機能など、さまざまな用語でそれらを比較できます。

イテレータは、foreachループではできないことを実行できます。たとえば、反復子がサポートしている場合、反復中に要素を削除できます。

_for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    Thing t = it.next();
    if (shouldBeDeleted(thing) {
        it.remove();
    }
} 
_

リストには、両方向に反復できる反復子もあります。 foreachループは、最初から最後まで繰り返します。

しかし、イテレータはより危険で読みにくいです。 foreachループが必要な場合は、最も読みやすいソリューションです。イテレータを使用すると、バグになる次のことができます。

_for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    System.out.println(it.next().getFoo());
    System.out.println(it.next().getBar());
} 
_

Foreachループでは、このようなバグは発生しません。

インデックスを使用して要素にアクセスする方が、配列で裏付けられたコレクションを使用すると、わずかに効率的です。しかし、気が変わってArrayListの代わりにLinkedListを使用すると、list.get(i)にアクセスするたびに、i番目まですべての要素をループする必要があるため、突然パフォーマンスが低下します。 。イテレータ(およびforeachループ)にはこの問題はありません。コレクション自体には独自のIterator実装があるため、指定されたコレクションの要素を反復処理するために常に可能な限り最良の方法を使用します。

私の一般的な経験則は、イテレーターの機能が本当に必要でない限り、foreachループを使用することです。ループ内のインデックスにアクセスする必要がある場合にのみ、配列を持つインデックスを持つforループを使用します。

108
JB Nizet

イテレーターの利点:

  • コレクションから要素を削除する機能。
  • next()およびprevious()を使用して前後に移動する機能。
  • hasNext()を使用して、さらに要素があるかどうかを確認する機能。

ループはCollectionを反復処理するように設計されているため、Collectionを反復処理する場合は、for-Eachなどのループを使用することをお勧めします。 Iteratorを使用できます。

17
Salah

番号(「i」など)でデータにアクセスする場合、配列を使用すると高速になります。要素に直接行くため

しかし、他のデータ構造(ツリー、リストなど)では、最初の要素からターゲット要素まで開始するため、より多くの時間が必要です。リストを使用するとき。時間O(n)が必要です。そのため、遅くなります。

イテレータを使用すると、コンパイラはあなたがどこにいるかを知っています。したがって、O(1)(現在の位置から開始するため)

最後に、直接アクセスをサポートする配列またはデータ構造のみを使用する場合(Javaのarraylistなど)。 「a [i]」は良いです。しかし、他のデータ構造を使用する場合、イテレーターはより効率的です

11
user3392677

イテレータと古典的なforループの主な違いは、反復している項目のインデックスにアクセスできるかどうかの明らかな違いは別として、イテレータを使用すると、基礎となるコレクション実装からクライアントコードが抽象化され、精巧。

コードがイテレータを使用する場合、この形式で

for(Item element : myCollection) { ... }

この形

Iterator<Item> iterator = myCollection.iterator();
while(iterator.hasNext()) {    
    Item element = iterator.next();    
    ... 
}

またはこのフォーム

for(Iterator iterator = myCollection.iterator(); iterator.hasNext(); ) {
   Item element = iterator.next();
   ...
}

あなたのコードが言っているのは、「コレクションのタイプとその実装は気にせず、その要素を反復処理できることだけです」と言っています。これは通常、より良いアプローチです。コードを分離するためです。

一方、従来のforループを使用している場合は、

for(int i = 0; i < myCollection.size(); i++) {
   Item element = myCollection.get(i);
   ...
}

あなたのコードは、コレクションのタイプを知る必要があると言っています。なぜなら、その要素を特定の方法で反復する必要があるためです。また、nullをチェックするか、反復の順序に基づいて結果を計算することもありますこれにより、コードがより脆弱になります。コレクションのタイプが変更されると、コードの動作に影響を与えるためです。

要約すると、違いは速度やメモリ使用量ではなく、コードを切り離して変更に対応する柔軟性を高めることです。

10
xburgos