PriorityQueue Dúvida

18 respostas
rmala_ti

Pessoal,
me deparei com a sequinte questão do Inquisition que pede qual a saída:

Queue q = new PriorityQueue(); q.offer(new String("hello ")); q.offer(new String("hi ") ); q.offer(new String("bye ")); for ( Object o : q ) { System.out.println(q.poll() + " " + q.size()); }

A resposta correta é:
Prints bye 2 followed by an Exception (ConcurrentModificationException)

Só que não consegui entender o motivo de não poder remover um elemento da Queue utilizando um for

Isso acontece com os métodos offer() e poll().

Alguém tem uma explicação?

Obrigado.

18 Respostas

T

Você não pode modificar algumas coleções enquanto está “andando” sobre elas (ou seja, quando você estiver usando um iterador).

rmala_ti

Entendi.

thingol, no caso dos conjuntos além de Queue existe outro tipo que ocorre a mesma situação(List, Set, Map)?
Eu to fazendo alguns testes mas até agora não vi nada de errado.

Obrigado.

T

ArrayList tem esse comportamento (você não pode usar “remove” assim, sem mais nem menos) mas CopyOnWriteArrayList não tem esse comportamento. De qualquer maneira, em uma prova de certificação, saber que classes têm esse comportamento não cai com certeza.

rmala_ti

valeu ai thingol pela explicação.

D

Eu não entendi porque imprime 2 o poll() remove o elemento da lista e depois lança a exceção??

rmala_ti

Eh isso tb não entendi.
Já que não eh permitido remover, pq já não lança a exceção?

T

Quem lança a exceção não é o remove ou poll ou outra rotina. É o iterator (não sei se é o hasNext ou o next que faz isso, é questão de ver o stack trace.)
Isto aqui:

for (Object o : q) {

equivale a:

for (Iterator it = q.iterator(); it.hasNext(); ) {
    Object o = it.next();
rmala_ti

Puts thingol, vc manja muito. :smiley:
valeu mesmo pela explicação.

ivandasilva

Aproveitando a deixa, como eu faco para obter os objetos atraves de um iterator em um PriorityQueue ou BlockingQueue…

Grato…

T
import java.util.*;

class Pessoa {
    public int idade;
    public String nome;
    public Pessoa (String nome_, int idade_) { idade = idade_; nome = nome_; }
    public String toString() { return "[" + nome + ", " + idade + " anos]"; }
}

class TestePriorityQueue {
    public static void main(String[] args) {
        // Esta é uma fila de prioridade onde os mais idosos ficam à frente dos mais novos
        PriorityQueue<Pessoa> pq = new PriorityQueue<Pessoa> (1, new Comparator<Pessoa>() {   
            public int compare (Pessoa p1, Pessoa p2) {
                return p2.idade - p1.idade;
            }
        });
        pq.add (new Pessoa ("Matusalem", 969));
        pq.add (new Pessoa ("Noé", 950));
        pq.add (new Pessoa ("Abraão", 175));
        pq.add (new Pessoa ("Adão", 930));
        pq.add (new Pessoa ("João Batista", 33));
        pq.add (new Pessoa ("Jesus", 33));
        // Imprimindo usando um iterador. Note que a ordem é indefinida.
        System.out.println ("Usando um iterator para percorrer a PriorityQueue");
        for (Iterator<Pessoa> it = pq.iterator(); it.hasNext(); ) {
            Pessoa p = it.next();
            System.out.println (p);
        }
        // Isto imprime os mais idosos antes dos mais jovens
        // Entretanto, "destrói" a fila (ou seja, esvazia-a).
        System.out.println ("Obtendo os elementos da PriorityQueue e imprimindo-os");
	for (Pessoa p = pq.poll(); p != null; p = pq.poll()) {
            System.out.println (p);
        }
    }
}

Saída:

Usando um iterator para percorrer a PriorityQueue
[Matusalem, 969 anos]
[Noé, 950 anos]
[Abraão, 175 anos]
[Adão, 930 anos]
[João Batista, 33 anos]
[Jesus, 33 anos]
Obtendo os elementos da PriorityQueue e imprimindo-os
[Matusalem, 969 anos]
[Noé, 950 anos]
[Adão, 930 anos]
[Abraão, 175 anos]
[Jesus, 33 anos]
[João Batista, 33 anos]
rmala_ti

thingol,
o mais estranho é que se tiver apenas 2 objetos no PriorityQueue, não sobe exceção alguma.

Queue q = new PriorityQueue(); q.offer(new String("hello ")); q.offer(new String("hi ") ); //q.offer(new String("bye ")); for ( Object o : q ) { System.out.println(q.poll() + " " + q.size()); }

T

Hum… só olhando o fonte de PriorityQueue. E é por isso que tal questão do Inquisition não tem a mínima chance de cair na prova desse jeito, porque é mal-formulada.

ivandasilva

Thingol, obrigado pelo exemplo!!

rmala_ti

Cara, muito estranho né.
tava fuçando nesse tópico http://www.guj.com.br/posts/list/30323.java
mas fiquei foi mais confuso.
Aproveitando já q vc disse, o nível do Inquisition tá muito acima da prova?

Abraço e obrigado novamente pelas respostas.

B

Cara, muito estranho né.
tava fuçando nesse tópico http://www.guj.com.br/posts/list/30323.java
mas fiquei foi mais confuso.
Aproveitando já q vc disse, o nível do Inquisition tá muito acima da prova?

Abraço e obrigado novamente pelas respostas.

Não sei se sua versão é igual à minha…mas eu peguei o Inquisition Trial SCJP6 e fiz uma média de 87% nos 3 simulados disponíveis lá.

Achei as questões muito simples. Mais fraco até q o TestKiller.

Acho q vi apenas UMA questão prática de Threads nos 3 simulados. Muito pouca coisa de Serialization. A maioria das perguntas é sobre O.O Concepts, Operators e Collections.

Já vi gente dizer q é mais pesado q o TestKiller, mas não achei.

(Talvez vc esteja se referindo ao Inquisition pra SCJP5).

D

Thingol quando vc instância o PriorityQueue o que significa este 1 passado ao construtor?

PriorityQueue<Pessoa> pq = new PriorityQueue<Pessoa> (1, new Comparator<Pessoa>()

Mauricio_Linhares

Thigol, eu acho que o “fail fast iterator” cai sim na certificação, porque é uma coisa de todos os iterators não concorrentes do Java.

Outra coisa, você pode remover itens da coleção usando o próprio iterator e isso não vai causar uma exceção, o que não pode é tentar usar um iterator criado antes da coleção ser alterada por ela mesma e não pelo iterator…

F

Olá galera. Estava pesquisando sobre a exceção ConcurrentModificationException e encontrei este tópico. Desculpe ressuscitá-lo depois de muito tempo, mas em relação ao que o rmalati questionou:

A questão é a seguinte rmalati: na classe PriorityQueue há um métod next() utilizado pelo iterador para retornar o próximo elemento:

public E next() {
            if (expectedModCount != modCount)
                throw new ConcurrentModificationException();
            if (cursor < size)
                return (E) queue[lastRet = cursor++];
            if (forgetMeNot != null) {
                lastRet = -1;
                lastRetElt = forgetMeNot.poll();
                if (lastRetElt != null)
                    return lastRetElt;
            }
            throw new NoSuchElementException();
        }

Há duas variáveis que ele utiliza para controle de modificações: expectedModCount e modCount. Essas duas variáveis ambas vão possuir o mesmo valor caso não haja nenhuma modificação. Como pode ser visto no método poll(), ele incrementa a variável modCount quando é invocado:

public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);
        return result;
    }

Conclusão: A primeira vez que o iterador invocar o método next(), o seguinte teste if (expectedModCount != modCount) throw new ConcurrentModificationException(); será feito e retornará false, fazendo com que o elemento seja impresso normalmente. Após o método poll() ser executado uma vez, ele irá modificar a variável modCount e a próxima vez que next() for chamado pelo iterador, o teste if (expectedModCount != modCount) throw new ConcurrentModificationException(); irá retornar true e fará com que lance ConcurrentModificationException.

Espero ter ajudado a esclarecer essa dúvida.

Criado 24 de março de 2009
Ultima resposta 3 de set. de 2011
Respostas 18
Participantes 7