簡體   English   中英

在迭代列表時從列表中刪除項目

[英]Removing items from a list while iterating over it

我已經看到了很多關於這個主題的問題,但沒有一個答案真正符合我的用例(除非我解釋錯了。)。 當迭代器迭代它時,是否可以從列表中刪除一個項目?

我想要實現的是有一個帶有音頻播放器的隊列。 迭代器在播放歌曲時遍歷隊列並阻塞。 歌曲可以在隊列播放時添加到隊列或從隊列中刪除。

我已經嘗試了上述想法並收到了異常ConcurrentModificationException 我還讀到,在迭代器迭代集合時改變集合是一種不好的做法。 我希望有人能指出我如何在迭代時正確地從方法調用中改變列表的正確方向。

最好的解決方案是不維護當前正在播放的歌曲。 通過Iterator播放的下一首歌曲。 相反,您可以創建一個專門的列表,該列表知道如何在修改時調整此指針。

這樣的類可能看起來像

class SongList extends AbstractList<Song> implements RandomAccess {
    final List<Song> backend = new ArrayList<>();
    int currentSong = -1;

    SongList() {}
    SongList(Collection<? extends Song> c) {
        backend.addAll(c);
    }
    // mandatory query methods

    @Override public int size() {
        return backend.size();
    }
    @Override public Song get(int index) {
        return backend.get(index);
    }

    // the "iterator"
    public Song nextSong() {
        if(++currentSong < size()) {
            return get(currentSong);
        }
        currentSong = -1;
        return null;
    }

    // modifying methods, which will adapt the pointer

    @Override public void add(int index, Song element) {
        backend.add(index, element);
        if(index <= currentSong) currentSong++;
    }
    @Override public Song remove(int index) {
        final Song removed = backend.remove(index);
        if(index <= currentSong) currentSong--;
        return removed;
    }

    @Override
    public boolean addAll(int index, Collection<? extends Song> c) {
        int old = size();
        backend.addAll(index, c);
        if(index <= currentSong) currentSong += size() - old;
        return true;
    }

    @Override protected void removeRange(int fromIndex, int toIndex) {
        backend.subList(fromIndex, toIndex).clear();
        if(fromIndex <= currentSong)
            currentSong = Math.max(fromIndex - 1, currentSong - toIndex + fromIndex);
    }

    // this will not change the pointer

    @Override public Song set(int index, Song element) {
        return backend.set(index, element);
    }

    // query methods overridden for performance

    @Override public boolean contains(Object o) {
        return backend.contains(o);
    }
    @Override public int indexOf(Object o) {
        return backend.indexOf(o);
    }
    @Override public Spliterator<Song> spliterator() {
        return backend.spliterator();
    }
    @Override public void forEach(Consumer<? super Song> action) {
        backend.forEach(action);
    }
    @Override public Object[] toArray() {
        return backend.toArray();
    }
    @Override public <T> T[] toArray(T[] a) {
        return backend.toArray(a);
    }
    @Override public String toString() {
        return backend.toString();
    }
}

AbstractList專門設計用於在幾個方法之上提供集合操作,因此我們只需要實現size()get(int)即可獲得可讀列表,並通過提供add(int, Song)remove(int)set(int, Song)我們已經完成了支持所有修改操作所需的一切。 提供其他方法只是為了提高性能,繼承的方法也可以。

該列表支持指向當前播放位置的單個指針,可以通過nextSong()對其進行迭代。 當到達末尾時,它將返回null並重置指針,以便下一個查詢重新開始。 addremove方法將調整指針,以便不再播放已經播放的歌曲(除非重新啟動整個列表)。

基於set的修改不適應指針,這意味着對列表進行sort時不會發生任何有意義的事情,一些策略是可以想象的,但至少當列表有重復時,不存在完美的行為。 與其他播放器軟件相比,似乎沒有人期望在播放時將列表顛倒過來時會有完美的表現。 至少,永遠不會有例外。

使用Iterator並調用其remove()方法:

List<String> myList = new ArrayList<>();
for (Iterator<String> i = myList.iterator(); i.hasNext();) {
    String next = i.next();
    if (some condition) {
        i.remove(); // removes the current element
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM