While working on Collections.singleton()
I found that it is not working as expteced. If you see the code below after forEachRemaining
code is neither throwing any exception
nor returning false
on itr.hasNext()
From Java Docs of forEachRemaining
Performs the given action for each remaining element until all elements have been processed
The out put of below code is: true,elem and I am expecting 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.
Looking at the code: Collections.singleton()
returns a SingletonSet
. If you call iterator()
on a SingletonSet
, the resulting iterator is of an anonymous class. The anonymous class overrides forEachRemaining
:
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
if (hasNext) {
action.accept(e);
hasNext = false;
}
}
Since your accept
throws an exception, hasNext
remains true
.
Note that the javadoc doesn't specify what should happen to forEachRemaining
if an exception is thrown; thus, it's possible that the next version of the runtime could put hasNext = false
above the action.accept(e)
, leading to a different result. So you can't count on the behavior one way or the other.
The iterator used by SingletonSet<E>
that is the class used to instance the singleton when Collections.singleton(T)
is invoked explains this behavior.
The iterator is provided by the static <E> Iterator<E> Collections.singletonIterator(E e)
method.
This has a hasNext
flag that pass to false
only if the consummer has returned without exception :
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;
}
}
...
}
The documentation of the method doesn't explicit this behavior but we could consider it as a safe check to step forward the iterator only it was correctly consumed.
But it gives however a hint about the exception handling :
Exceptions thrown by the action are relayed to the caller.
So if a exception occurs during forEachRemaining()
, there is no guarantee on the state of the iterator as the exception is not caught by forEachRemaining()
but forwarded to the caller.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With