Collections.singleton () and forEachRemaining - Java 8

While working on it, Collections.singleton()I found that it does not work as expteced. If you see the code below after forEachRemaining, the code does not throw any exceptionand does not return falsetoitr.hasNext()

From Java docs forEachRemaining

Performs a task for each remaining item until all items are processed

The code output is below: true , elem , and I expect false , NoSuchElementException

public class Test {
        public static void main(String[] args) {
        Collection<String> abc = Collections.singleton("elementsItr");
        final Iterator<String> itr = abc.iterator();
        try {
            itr.forEachRemaining((e) -> {
                throw new RuntimeException();
            });
        } catch (RuntimeException e) {

        }
        System.out.println(itr.hasNext());
        System.out.println(itr.next());

    }
}

Please help me understand this behavior.

+4
source share
4 answers

: Collections.singleton() a SingletonSet. iterator() SingletonSet, . forEachRemaining:

public void forEachRemaining(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    if (hasNext) {
        action.accept(e);
        hasNext = false;
    }
}

accept , hasNext true.

, javadoc , forEachRemaining, ; , , hasNext = false action.accept(e), . .

+4

, SingletonSet<E>, , singleton Collections.singleton(T), .
static <E> Iterator<E> Collections.singletonIterator(E e).

hasNext, false, consummer :

static <E> Iterator<E> singletonIterator(final E e) {
   ...   
        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            if (hasNext) {
                action.accept(e);
                hasNext = false;
            }
        }
   ...
}

, , . :

, , .

, forEachRemaining() , , forEachRemaining(), .

+2

OpenJDK JDK-8166446. :

, . http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html#forEachRemaining-java.util.function.Consumer-

"" , . , . , , . "

, " " , accept() .

, singleton: . ArrayList.Iterator :         public void forEachRemaining ( ) {........             }             while (i!= size && modCount == expectedModCount) {                 consumer.accept((E) elementData [i ++]);             }             // ,             cursor = i;             lastRet = - 1;........}

---------- , .

forEachRemaining(), .

JDK 9 , JDK 9-ea, :

Is empty: false
Exception in thread "main" java.util.NoSuchElementException
        at java.base/java.util.Collections$1.next(Collections.java:4698)
        at Test.main(Test.java:18)
+2
source

If you look carefully, you will see that you get a plain iterator ( Collections#singletonIterator()).

The code looks like this:

static <E> Iterator<E> singletonIterator(final E e) {
    return new Iterator<E>() {
        private boolean hasNext = true;
        public boolean hasNext() {
            return hasNext;
        }
        public E next() {
            if (hasNext) {
                hasNext = false;
                return e;
            }
            throw new NoSuchElementException();
        }
        public void remove() {
            throw new UnsupportedOperationException();
        }
        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            if (hasNext) {
                action.accept(e);
                hasNext = false;
            }
        }
    };
}

Here you have the following lines:

action.accept(e);
hasNext = false;

As you throw RuntimeExceptioninside your lambda (called with accept(e)), the variable is hasNextnever set to falsebecause the line is not reached.

+1
source

Source: https://habr.com/ru/post/1681667/


All Articles