Is there any danger when creating an .accept () action for more than one element in a Spliterator.tryAdance () implementation?

The javadoc Spliterator mentions that:

The delimiter can move through individual elements (tryAdvance ()) or sequentially in bulk (forEachRemaining ()).

Then go to javadoc tryAdvance() , which states that:

If the remaining element exists, it performs the specified action on it, returning true; else returns false.

Perhaps I am reading something incorrectly, but for me it seems that provided that one or more elements remaining, Consumer should use only each .accept() argument as an argument before returning true , and if if, say, I have two arguments that are available immediately, and then I cannot:

 action.accept(arg1); action.accept(arg2); return true; 

In this project , I rewrote the width of the first separator so that it now reads:

 // deque is a Deque<Iterator<T>> @Override public boolean tryAdvance(final Consumer<? super T> action) { Iterator<T> iterator; T element; while (!deque.isEmpty()) { iterator = deque.removeFirst(); while (iterator.hasNext()) { element = iterator.next(); deque.add(fn.apply(element)); action.accept(element); } } return false; } 

In short, I am doing an action to accept all arguments, and then return false ... and the test, although fairly simple, still succeeds ( link ).

Note that .trySplit() always returns null ; and the delimiter has chacacteristics DISTINCT , ORDERED and NONNULL .

So, is there a use of a thread in which the code above will not work due to the above method, which consumes all the elements at the same time?

+5
source share
1 answer

Your assumption that tryAdvance() should consume only one element is correct. This, however, does not mean that you will immediately notice breaches of the contract. When you test the use of operations such as .collect(Collectors.toList()) , it is unlikely to detect such a violation, since most operations that consume all elements call forEachRemaining() on the separator, default implemented as :

The default implementation repeatedly calls tryAdvance (java.util.function.Consumer) until it returns false.

Obviously, this does not matter for this method.

The stream structure will call tryAdvance() when performing lazy operations. Therefore, when you use .peek(System.out::println).findFirst() , you may notice a difference when your implementation of tryAdvance() more than one value. However, given the current implementation, the result is the correct first element. Apparently, the consumer implementing the implementation ignores subsequent values ​​after receiving the value.

This may be due to other implementation details, such as those discussed in Why filter () after flatMap () is not completely lazy in Java threads? . If the thread implementation itself puts forward more values ​​than is necessary under certain circumstances, the receiving party in the same implementation should be prepared to handle this case.

But it must be emphasized that this is the behavior of one specific implementation of the Stream API. Another implementation or the next version of Java may rely on the correct implementation of tryAdvance . In addition, there may be use cases for Spliterator other than streams.


So, I finally found an example of an operation that breaks with your Spliterator :

 for(Iterator<?> it=Spliterators.iterator(spliterator); it.hasNext();) { System.out.println(it.next()); } 
+7
source

All Articles