2014-10-21 18 views
7

Ho una struttura di dati come questo:Come rimuovere elementi da una coda in Java con un ciclo

BlockingQueue casella di posta = new LinkedBlockingQueue();

Sto cercando di fare questo:

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

Ovviamente il contenuto del ciclo interferiscono con i limiti e un errore si attiva, quindi normalmente fare questo:

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

Ma purtroppo BlockingQueue non ha una funzione per ottenere o rimuovere un elemento per indice, quindi sono bloccato. Qualche idea?

Modifica - Alcuni chiarimenti: Uno dei miei obiettivi è mantenere lo stesso ordine in modo che spuntare dalla testa e rimetterlo in coda non va bene. Inoltre, anche se nessun altro thread rimuoverà la posta da una casella di posta, verrà aggiunto ad esso, quindi non voglio trovarmi nel mezzo di un algoritmo di rimozione, avere qualcuno che mi invii posta e quindi fare in modo che si verifichi un'eccezione.

Grazie in anticipo!

+0

controllare la risposta che ho postato .. – UDPLover

risposta

3

È possibile p̶o̶p̶ poll e p̶u̶s̶h̶ offer tutti gli elementi della coda finché non si effettua un ciclo completo sulla coda. Ecco un esempio:

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

Nota che questo approccio funziona solo se questo codice viene eseguito in un singolo thread e non c'è nessun altro thread che rimuove gli elementi da questa coda.

Forse è necessario verificare se si desidera realmente eseguire il polling o prendere gli elementi da BlockingQueue. Simile per offerta e put.

Maggiori informazioni:


Un altro approccio meno buggy sta usando una raccolta temporanea, non necessariamente concomitante, e memorizzare gli elementi che ancora bisogno nel coda. Ecco un esempio iniziale:

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 * corretto *. Tuttavia, OP non ci notifica se altri thread rimuovono gli articoli dalla coda. –

+0

@ScaryWombat bene, non leggo altre domande prev, quindi mi manca questo contesto. –

+0

Forse mi manca il tuo punto, ma non 'peek' restituisce null se la coda è vuota (non bloccante)? Nella tua versione precedente, se la coda era vuota prima di entrare nel tuo loop, avrebbe generato un NPE. –

0

Ho controllato le soluzioni pubblicate e penso di aver trovato una versione che serve ai miei scopi. Cosa ne pensi di questo?

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

Modifica: una nuova soluzione che può essere senza problemi. Cosa ne pensate voi ragazzi?

while(true) 
{ 
    boolean badNewRemains = false; 

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

    if(!badNewRemains) 
     break; 
} 
+0

La dimensione della coda può cambiare poiché altri thread aggiungono più messaggi alla coda. Stai attento a questo. –

+0

Sì, ora vedo che gli elementi possono essere fuori ordine se arrivano nuovi dati nel mezzo. Ho dato un altro tentativo sopra. – Josh

0

È possibile implementare facilmente la coda per le proprie esigenze. E sarà necessario, se l'API fornita non ha tali caratteristiche.

Uno come:

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

La coda implementato sarà thread-safe, sia push o pop. Inoltre è possibile modificare la coda per ulteriori operazioni. E consentirà di accedere al metodo removeBadMailsInstantly da più thread (thread-safe). E imparerai anche concetti di multithreading.

+0

Se ci sono molti pezzi di posta che usano tutti questa classe e 'private final Object lock = new Object();', si bloccheranno a vicenda o ognuno di essi sarà in qualche modo univoco? – Josh

+0

Downvoter, motivo per downvoting? – UDPLover

+0

@Josh Fondamentalmente è necessario creare un'istanza di questo MailQueue e lasciare che tutti i thread utilizzino questa istanza, quindi ci sarà un solo blocco, tutti i thread che utilizzano lo stesso blocco. – UDPLover