Why does the iterator.hasNext not work with BlockingQueue?

11,103

Solution 1

1) Is this bad design, or wrong expectation?

Wrong expectations since it would otherwise violate the contract of Iterator which on Iterator.next() says: Throws: NoSuchElementException - iteration has no more elements. If next() would block the exception would never be thrown.

2) Is there a way to use the blocking methods

Yes, for instance by extending the class and overriding the next and hasNext methods to use blocking routines instead. Note that hasNext would need to always return true in this case - which again violates the contract.

Solution 2

Just don't use iterators with Queues. Use peek() or poll() instead or take() if it's a BlockingQueue:

void consume() {
    new Thread() {
        @Override
        public void run() {
            Object value;
            // actually, when using a BlockingQueue,
            // take() would be better than poll()
            while ((value=q.poll())!=null)
                System.out.println(value);
        }
    }.start();
}

A Queue is an Iterable because it is a Collection and hence needs to provide an iterator() method, but that shouldn't ever be used, or you shouldn't be using a Queue in the first place.

Solution 3

if an iterator blocked on hasNext then the iteration would never finish unless you explicitly broke out of it, this would be quite a strange design.

In any case the LinkedBlockingQueue javadoc has this to say

Returns an iterator over the elements in this queue in proper sequence. 
The returned <tt>Iterator</tt> is a "weakly consistent" iterator that will 
never throw {@link ConcurrentModificationException}, and guarantees to 
traverse elements as they existed upon construction of the iterator, and 
may (but is not guaranteed to) reflect any modifications subsequent to 
construction.
Share:
11,103
VGDIV
Author by

VGDIV

Updated on June 27, 2022

Comments

  • VGDIV
    VGDIV almost 2 years

    I was trying to use the iterator methods on a BlockingQueue and discovered that hasNext() is non-blocking - i.e. it will not wait until more elements are added and will instead return false when there are no elements.

    So here are the questions :

    1. Is this bad design, or wrong expectation?
    2. Is there a way to use the blocking methods of the BLockingQueue with its parent Collection class methods (e.g. if some method were expecting a collection, can I pass a blocking queue and hope that its processing will wait until the Queue has more elements)

    Here is a sample code block

    public class SomeContainer{
         public static void main(String[] args){
            BlockingQueue bq = new LinkedBlockingQueue();
            SomeContainer h = new SomeContainer();
            Producer p = new Producer(bq);
            Consumer c = new Consumer(bq);
            p.produce();
            c.consume();
        }
    
        static class Producer{
            BlockingQueue q;
            public Producer(BlockingQueue q) {
                this.q = q;
            }
    
            void produce(){
            new Thread(){
                public void run() {
                for(int i=0; i<10; i++){
                    for(int j=0;j<10; j++){
                        q.add(i+" - "+j);
                    }
                    try {
                        Thread.sleep(30000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                };
            }.start();
            }
        }
    
    
        static class Consumer{
             BlockingQueue q;
    
             public Consumer(BlockingQueue q) {
                 this.q = q;
             }
    
            void consume() {
                new Thread() {
                    public void run() {
                        Iterator itr = q.iterator();
                        while (itr.hasNext())
                            System.out.println(itr.next());
                    }
                }.start();
            }
            }
        }
    

    This Code only prints the iteration once at the most.

  • planetjones
    planetjones almost 13 years
    Well yes it will but the question was how could it work via the java.util.Iterator - i think we can agree the BlockingQueue.take() should really be used here.
  • VGDIV
    VGDIV almost 13 years
    >>A Queue is an Iterable because it is a Collection and hence needs to provide an iterator() method, but that shouldn't ever be used<< Couldn't this be an atleast partial case of bad design in Java?
  • VGDIV
    VGDIV almost 13 years
    What if we consider itr.next() in isolation i.e. - would good design principles dictate that BlockingQueue implementation ensure blocking if there were no objects on the queue? However I see your point that hasNext() contract would be violated with or without this.
  • Sean Patrick Floyd
    Sean Patrick Floyd almost 13 years
    @Varun yes and no. It's a valid use case to add all remaining members of a Queue to a different collection, so the iterator() makes sense there, but just not for normal everyday use.
  • Johan Sjöberg
    Johan Sjöberg almost 13 years
    I think not. Iterator is used to view a snapshot of a list. If implementing it blocking it could potentially block forever which in your case might be useful, but doesn't serve the purpose it was included for.
  • WestCoastProjects
    WestCoastProjects almost 10 years
    This is the right answer. I was going down the wrong path myself - considering to use an iterator() for queue. But that is not the way to go.