How to remove elements from a queue in Java with a loop

I have a data structure like this:

BlockingQueue mailbox = new LinkedBlockingQueue();

I'm trying to do this:

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

Obviously the contents of the loop interfere with the bounds and a error is triggered, so I would normally do this:

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

But sadly BlockingQueue's don't have a function to get or remove an element by index, so I'm stuck. Any ideas?

Edit - A few clarifications: One of my goals is the maintain the same ordering so popping from the head and putting it back into the tail is no good. Also, although no other threads will remove mail from a mailbox, they will add to it, so I don't want to be in the middle of an removal algorithm, have someone send me mail, and then have an exception occur.

Thanks in advance!


You may p̶o̶p̶ poll and p̶u̶s̶h̶ offer all the elements in your queue until you make a complete loop over your queue. Here's an example:

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();
}

Note that this approach will work only if this code is executed in a single thread and there's no other thread that removes items from this queue.

Maybe you need to check if you really want to poll or take the elements from the BlockingQueue. Similar for offer and put.

More info:

  • Java BlockingQueue take() vs poll()
  • LinkedBlockingQueue put vs offer

  • Another less buggy approach is using a temporary collection, not necessarily concurrent, and store the elements you still need in the queue. Here's a kickoff example:

    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);
    }
    

    I looked over the solutions posted and I think I found a version that serves my purposes. What do you think about this one?

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

    Edit: A new solution that may be problem free. What you guys think?

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

    You can easily implement queue for your need. And you will need to, if API provided doesn't have such features.

    One like:

    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;
            }
        }
    }
    

    The implemented queue will be thread-safe, whether push or pop. Also you can edit queue for more operations. And it will allow to access removeBadMailsInstantly method by multiple threads (thread-safe). And you will also learn concepts of multithreading.

    链接地址: http://www.djcxy.com/p/23192.html

    上一篇: data.table的包装函数

    下一篇: 如何使用循环从Java中的队列中删除元素