I have a method which performs processing on a stream. Part of that processing needs to be done under the control of a lock - one locked section for processing all the elements - but some of it doesn't (and shouldn't be because it might be quite time-consuming). So I can't just say:
Stream<V> preprocessed = Stream.of(objects).map(this::preProcess);
Stream<V> toPostProcess;
synchronized (lockObj) {
toPostProcess = preprocessed.map(this::doLockedProcessing);
}
toPostProcess.map(this::postProcess).forEach(System.out::println);
because the calls to doLockedProcessing
would only be executed when the terminal operation forEach
is invoked, and that is outside the lock.
So I think I need to make a copy of the stream, using a terminal operation, at each stage so that the right bits are done at the right time. Something like:
Stream<V> preprocessed = Stream.of(objects).map(this::preProcess).copy();
Stream<V> toPostProcess;
synchronized (lockObj) {
toPostProcess = preprocessed.map(this::doLockedProcessing).copy();
}
toPostProcess.map(this::postProcess).forEach(System.out::println);
Of course, the copy()
method doesn't exist, but if it did it would perform a terminal operation on the stream and return a new stream containing all the same elements.
I'm aware of a few ways of achieving this:
(1) Via an array (not so easy if the element type is a generic type):
copy = Stream.of(stream.toArray(String[]::new));
(2) Via a list:
copy = stream.collect(Collectors.toList()).stream();
(3) Via a stream builder:
Stream.Builder<V> builder = Stream.builder();
stream.forEach(builder);
copy = builder.build();
What I want to know is: which of these methods is the most efficient in terms of time and memory? Or is there another way which is better?