2014-10-21 1 views
7

У меня есть структура данных, как это:Как удалить элементы из очереди в Java с петлей

BlockingQueue почтового ящика = новый 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 нет функции для получения или удаления элемента по индексу, поэтому я застрял. Есть идеи?

Редактировать - Несколько пояснений: Одна из моих целей - поддерживать тот же порядок, что и выскакивание с головы, и возвращение его обратно в хвост не является хорошим. Кроме того, хотя никакие другие потоки не будут удалять почту из почтового ящика, они будут добавлены к ней, поэтому я не хочу быть посреди алгоритма удаления, попросить кого-нибудь отправить мне письмо, а затем возникнет исключение.

Заранее спасибо!

+0

проверить ответ, который я опубликовал .. – UDPLover

ответ

3

Вы можете 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. Похоже на предложение и предложение.

Подробнее:


Другой менее глючный подход использует временный сбор, не обязательно одновременное и хранить элементы, которые еще нуждаются в очередь. Вот пример запуска:

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

@ScaryWombat * исправленный *. Тем не менее, OP не уведомляет нас, если другие потоки также удаляют элементы из очереди. –

+0

@ScaryWombat хорошо, я не читаю ни одного другого предыдущего вопроса, поэтому мне не хватает этого контекста. –

+0

Возможно, мне не хватает вашей точки, но не 'peek' возвращает null, если очередь пуста (неблокирующая)? В предыдущей версии, если очередь была пустой до ввода вашего цикла, она бы выбросила NPE. –

0

Я просмотрел решения, и я думаю, что нашел версию, которая служит моим целям. Что вы думаете об этом?

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

Размер очереди может измениться, так как другие потоки добавляют в очередь больше писем. Будьте осторожны. –

+0

Да, теперь я вижу, что элементы могут быть упорядочены, если новые данные поступают посередине. Я дал ему еще одну попытку выше. – Josh

0

Вы можете легко реализовать очередь для своих нужд. И вам понадобится, если предоставленный 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

Если есть много писем, все из которых используют этот класс и «закрытый конечный объект Lock = new Object();», будут ли они блокировать друг друга или каждый замок каким-то образом будет уникальным? – Josh

+0

Downvoter, причина для downvoting? – UDPLover

+0

@Josh В принципе, вам нужно создать один экземпляр этого MailQueue, и пусть все потоки будут использовать этот экземпляр, так что будет только один замок, все потоки будут использовать одну и ту же блокировку. – UDPLover

Смежные вопросы