web-dev-qa-db-ja.com

Javaループを使用してキューから要素を削除する方法

私はこのようなデータ構造を持っています:

BlockingQueueメールボックス= new LinkedBlockingQueue();

私はこれをやろうとしています:

for(Mail mail: mailbox)
{
    if(badNews(mail))
    {
        mailbox.remove(mail);
    }
}

明らかに、ループの内容が境界に干渉し、エラーがトリガーされるため、通常は次のようにします。

for(int i = 0;  i < mailbox.size(); i++)
{
    if(badNews(mailbox.get(i)))
    {
        mailbox.remove(i);
        i--;
    }
}

しかし悲しいことに、BlockingQueueにはインデックスによって要素を取得または削除する関数がないため、行き詰まります。何か案は?

編集-いくつかの説明:私の目標の1つは、同じ順序を維持することです。そのため、頭から飛び出して尾に戻すのは良くありません。また、他のスレッドはメールボックスからメールを削除しませんが、が追加されるので、削除アルゴリズムの途中になりたくないので、誰かにメールを送ってもらい、例外を発生させます。

前もって感謝します!

8
Josh

キューを完全にループするまで、キュー内のすべての要素をp—o—p— poll およびp—u—s—h— offer にすることができます。次に例を示します。

Mail firstMail = mailbox.peek();
Mail currentMail = mailbox.pop();
while (true) {
    //a base condition to stop the loop
    Mail tempMail = mailbox.peek();
    if (tempMail == null || tempMail.equals(firstMail)) {
        mailbox.offer(currentMail);
        break;
    }
    //if there's nothing wrong with the current mail, then re add to mailbox
    if (!badNews(currentMail)) {
        mailbox.offer(currentMail);
    }
    currentMail = mailbox.poll();
}

このアプローチは、このコードが単一のスレッドで実行され、このキューからアイテムを削除する他のスレッドがない場合にのみ機能することに注意してください。

たぶん、BlockingQueueから要素を本当にポーリングまたは取得するかどうかを確認する必要があります。オファーとプットについても同様です。

より詳しい情報:


もう1つのバグの少ないアプローチは、必ずしも同時ではない一時的なコレクションを使用し、まだ必要な要素をキューに格納することです。キックオフの例は次のとおりです。

List<Mail> mailListTemp = new ArrayList<>();
while (mailbox.peek() != null) {
    Mail mail = mailbox.take();
    if (!badNews(mail)) {
        mailListTemp.add(mail);
    }
}
for (Mail mail : mailListTemp) {
    mailbox.offer(mail);
}
3
Luiggi Mendoza

必要に応じてキューを簡単に実装できます。また、提供されているAPIにそのような機能がない場合は、そうする必要があります。

のようなもの:

import Java.util.Iterator;
import Java.util.LinkedList;


class Mail {
    boolean badMail;
}

class MailQueue {
    private LinkedList<Mail> backingQueue = new LinkedList<>();
    private final Object lock = new Object();

    public void Push(Mail mail){
        synchronized (lock) {
            backingQueue.addLast(mail);
            if(backingQueue.size() == 1){
                // this is only element in queue, i.e. queue was empty before, so invoke if any thread waiting for mails in queue.
                lock.notify();
            }
        }
    }

    public Mail pop() throws InterruptedException{
        synchronized (lock) {
            while(backingQueue.isEmpty()){
                // no elements in queue, wait.
                lock.wait();
            }
            return backingQueue.removeFirst();
        }
    }

    public boolean removeBadMailsInstantly() {
        synchronized (lock) {
            boolean removed = false;
            Iterator<Mail> iterator = backingQueue.iterator();

            while(iterator.hasNext()){
                Mail mail = iterator.next();
                if(mail.badMail){
                    iterator.remove();
                    removed = true;
                }
            }

            return removed;
        }
    }
}

実装されたキューは、プッシュかポップかに関係なく、スレッドセーフになります。また、キューを編集してさらに操作することもできます。また、複数のスレッドでremoveBadMailsInstantlyメソッドにアクセスできるようになります(スレッドセーフ)。また、マルチスレッドの概念についても学びます。

0
nullptr

投稿されたソリューションを調べたところ、自分の目的に合ったバージョンが見つかったと思います。これについてどう思いますか?

int size = mailbox.size();
for(int i = 0; i < size; i++)
{
    Mail currentMail = mailbox.poll();
    if (!badNews(currentMail))
        mailbox.offer(currentMail);
}

編集:問題がないかもしれない新しい解決策。君たちはどう思う?

while(true)
{
    boolean badNewRemains = false;

    for(Mail mail: mailbox)
    {
        if(badNews(mail))
        {
            badNewRemains = true;
            mailbox.remove(mail);
            break;
        }
    }

    if(!badNewRemains)
        break;
}
0
Josh