2014-01-12 59 views
5

我試圖使用迭代器的迭代次數作爲計數器,但想知道這樣做的後果。迭代器可以更改它正在迭代的集合嗎? Java

private int length(Iterator<?> it) { 
    int i = 0; 

    while(it.hasNext()) { 
     it.next(); 
     i++; 
    } 

    return i; 
} 

這工作正常,但我擔心迭代器可能會在幕後做些什麼。也許當我迭代堆棧時,它會將項目從堆棧中彈出,或者如果我使用優先級隊列,並且它會修改優先級。

的Javadoc說這對迭代器:

下一個
鄂中迭代的next()
返回的下一個元素。
返回:
在迭代
的下一個元素拋出:
NoSuchElementException - 如果迭代有沒有更多的元素

我沒有看到,遍歷這個未知的集合將不會修改擔保它。我是在想不切實際的邊緣案例,還是這是一個問題?有沒有更好的辦法?

+0

如果有人向你傳遞'Iterator',他們希望你調用'next()',不管它的副作用是什麼。 –

回答

5

Iterator只是提供了一個接口轉換成某種流的,因此它不僅是完全可能next()以某種方式破壞數據,但是是唯一的,不可替代的,它甚至可能在Iterator數據。

我們可以拿出更直接的例子,但一個簡單的例子是IteratorDirectoryStream。雖然DirectoryStream在技術上是Iterable,只允許一個Iterator要構造,所以如果你想做到以下幾點:

Path dir = ... 
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { 
    int count = length(stream.iterator()); 
    for (Path entry: stream) { 
    ... 
    } 
} 

你會得到在foreach塊中的異常,因爲流只能使用一次迭代。總之,您的length()方法可能會更改對象並丟失數據。

此外,沒有理由Iterator必須與一些單獨的數據存儲相關聯。以例如answer I gave a few months之前提供一個乾淨的方式來選擇n隨機數字。通過使用無限的Iterator,我們可以提供,過濾並隨意傳遞任意大量的隨機數據,無需一次全部存儲,甚至可以在需要時計算它們。因爲Iterator不支持任何數據結構,查詢它顯然是破壞性的。

現在說,這些例子不會讓你的方法不好。請注意,Guava library(每個人都應該使用)提供Iterators類,其中包含上面詳細描述的行爲,稱爲size()以符合集合框架。這樣,這些方法的用戶就會意識到他們正在處理什麼類型的數據,並避免進行粗心的調用,例如試圖計算他們知道不能被替換的Iterator中的結果數量。

+1

+1查找反例! – assylias

2

不,迭代集合不會修改集合。 Iterator類確實有一個remove()方法,這是在迭代過程中從集合中刪除元素的唯一安全方法。但只需撥打hasNext()next()將不會修改集合。

請記住,如果修改next()返回的對象,這些更改將出現在您的集合中。

+1

這是一個合理的行爲,但是當被迭代後會被「修改」的集合仍然會與迭代契約(顯然)是一致的,這就是問題所在。 – assylias

+0

對集合(集合框架中的某些東西)進行迭代不會修改集合,但這並不意味着某些任意定義的'Iterator'或'Iterable'如果它選擇了則不能這樣做。考慮一個返回隨機數的Iterator;一旦調用了next(),那個數字就被有效地「消失」了。 – dimo414

0

想一想 - 返回值的方法是(如果它們寫入正確)accessor方法,也就是說它們只返回數據。他們不會修改它(它們不是增變器方法)。

下面是我的磁盤上的一個示例,其中介紹瞭如何實現迭代器。正如你所看到的,沒有值被實際修改。

public class ArraySetIterator implements Iterator 
{ 
    private int nextIndex; 
    private ArraySet theArraySet; 

    public ArraySetIterator (ArraySet a) 
    { 
     this.nextIndex = 0; 
     this.theArraySet = a; 
    } 

    public boolean hasNext() 
    { 
     return this.nextIndex < this.theArraySet.size(); 
    } 

    public Object next() 
    { 
     return this.theArraySet.get(this.nextIndex++); 
    } 
} 
+1

是*一般*'Iterator'不修改。 OP正在詢問特殊情況。不修改其數據的示例'Iterator'是無關緊要的。沒有一個規則,即返回值的方法永遠不會改變狀態。 – dimo414

4

據我所知道的,Collection規範並沒有明確指出遍歷集合不修改它,但在標準庫中沒有的類表明行爲(實際上至少有一種格式,見dimo414's answer),所以任何班級都會高度懷疑。我不認爲你需要擔心這一點。

請注意,Guava庫以與您相同的方式執行Iterators.size()Iterables.size(),顯然他們發現它在一般情況下是安全的。

+1

+1。請注意,即使'java.util.Iterator'確實指定了類似的東西,它實際上也不是綁定的要求。它經常發生一個類實際上不符合其接口的契約。 (例如,JDK的java.util.IdentityHashMap完全有意地違反了java.util.Map的通用契約。) – ruakh

+1

Collection以外的對象可以具有或可以是Iterator 。 – dimo414