Существует вопрос о том, должны ли методы Java возвращать Коллекции или потоки, на который Брайан Гетц отвечает, что даже для конечных последовательностей обычно следует отдавать предпочтение потокам.
Но мне кажется, что в настоящее время многие операции над потоками, которые приходят из других мест, не могут быть безопасно выполнены, а защитные кодовые охранники невозможны, потому что потоки не раскрывают, являются ли они бесконечными или неупорядоченными.
Если параллельность была проблемой для операций, которые я хочу выполнить в Stream(), я могу вызвать isParallel() для проверки или последовательности, чтобы убедиться, что вычисления выполняются параллельно (если я не забыл).
Но если упорядоченность или конечность (размерность) имеют отношение к безопасности моей программы, я не могу писать меры безопасности.
Предполагая, что я использую библиотеку, реализующую этот фиктивный интерфейс:
public interface CoordinateServer {
public Stream<Integer> coordinates();
// example implementations:
// finite, ordered, sequential
// IntStream.range(0, 100).boxed()
// final AtomicInteger atomic = new AtomicInteger();
// // infinite, unordered, sequential
// Stream.generate(() -> atomic2.incrementAndGet())
// infinite, unordered, parallel
// Stream.generate(() -> atomic2.incrementAndGet()).parallel()
// finite, ordered, sequential, should-be-closed
// Files.lines(Path.path("coordinates.txt")).map(Integer::parseInt)
}
Тогда какие операции я могу безопасно вызывать в этом потоке, чтобы написать правильный алгоритм?
Кажется, если я, возможно, захочу записать элементы в файл в качестве побочного эффекта, мне нужно беспокоиться о параллельности потока:
// if stream is parallel, which order will be written to file?
coordinates().peek(i -> {writeToFile(i)}).count();
// how should I remember to always add sequential() in such cases?
А также, если он параллелен, на основе какого пула потоков он параллелен?
Если я хочу отсортировать поток (или другие операции без короткого замыкания), мне как-то нужно быть осторожным с тем, что он бесконечен:
coordinates().sorted().limit(1000).collect(toList()); // will this terminate?
coordinates().allMatch(x -> x > 0); // will this terminate?
Я могу ввести ограничение перед сортировкой, но какое это должно быть магическое число, если я ожидаю конечный поток неизвестного размера?
Наконец, может быть, я хочу вычислить параллельно, чтобы сэкономить время, а затем собрать результат:
// will result list maintain the same order as sequential?
coordinates().map(i -> complexLookup(i)).parallel().collect(toList());
Но если поток не упорядочен (в этой версии библиотеки), то результат может быть искажен из-за параллельной обработки. Но как я могу защититься от этого, кроме как не использовать параллель (что противоречит цели производительности)?
Коллекции явно указывают, являются ли они конечными или бесконечными, имеют порядок или нет, и они не несут с собой режим обработки или пулы потоков. Это кажется ценным свойством для API.
Кроме того, Потоки иногда нужно закрывать, но чаще всего нет. Если я использую поток из метода (или из параметра метода), должен ли я обычно вызывать close?
Кроме того, потоки могли быть уже потреблены, и было бы хорошо иметь возможность корректно обрабатывать этот случай, поэтому было бы хорошо использовать проверить, не был ли уже использован поток;
Я бы хотел, чтобы какой-нибудь фрагмент кода можно было использовать для проверки предположений о потоке перед его обработкой, например >
Stream<X> stream = fooLibrary.getStream();
Stream<X> safeStream = StreamPreconditions(
stream,
/*maxThreshold or elements before IllegalArgumentException*/
10_000,
/* fail with IllegalArgumentException if not ordered */
true
)
Я думаю, вы могли бы использовать для этого характеристики потока - см. вопрос это для более подробной информации.
Спасибо, я не знал о характеристиках сплиттератора. Тем не менее, они не похожи на что-то, что можно использовать в программировании приложений (скорее на детали реализации Stream).
возможно, это ответит на некоторые из ваших вопросов baeldung.com/java-поток-заказ
Как говорит сам Брайан в опубликованном вами ответе: «Единственный случай, когда вы должны вернуть коллекцию, - это когда существуют строгие требования согласованности». Требование конечности является одним из них.
@daniu: Но он прямо предлагает использовать Streams для конечных данных, а это означает, что конечность не является для него строгим требованием согласованности.
Если только вы не предполагаете, что автор метода знает все варианты использования (текущее и будущее) метода и, таким образом, знает, что для клиентов конечность не является требованием согласованности.




Посмотрев немного на вещи (некоторые эксперименты и здесь), насколько я вижу, нет способа точно узнать, является ли поток конечным или нет.
Более того, иногда даже он не определяется, кроме как во время выполнения (например, в java 11 — IntStream.generate(() -> 1).takeWhile(x -> externalCondition(x))).
Что вы можете сделать, это:
Вы можете с уверенностью узнать, является ли оно конечным, несколькими способами (обратите внимание, что получение false для них не означает, что оно бесконечно, а только то, что это может быть так):
stream.spliterator().getExactSizeIfKnown() - если у этого есть известный точный размер, он конечен, иначе он вернет -1.
stream.spliterator().hasCharacteristics(Spliterator.SIZED) - если это так, SIZED вернет true.
Вы можете обезопасить себя, предполагая худшее (зависит от вашего случая).
stream.sequential()/stream.parallel() - явно укажите предпочитаемый тип потребления.С потенциально бесконечным потоком предположите наихудший случай для каждого сценария.
stream.filter(tweet -> isByVenkat(tweet)).findAny() — он будет повторяться до тех пор, пока не появится такой твит (или навсегда).stream.limit(x) перед вызовом вашей операции (collect или allMatch или аналогичной), где x — количество попыток, которые вы готовы терпеть.После всего этого я просто упомяну, что я думаю, что возврат потока, как правило, не очень хорошая идея, и я бы постарался избежать этого, если нет больших преимуществ.
Я думаю, что .splititerator() - это метод, а не общедоступное поле. Также вы можете скопировать проверку SIZED для ORDERED, я думаю? Я думаю, что должна быть возможность иметь счетчик для элементов по мере обработки потока, чтобы даже для потенциально бесконечных потоков он мог генерировать исключение, если испускается больше элементов, чем я максимально ожидал (конечно, за счет производительности). Еще хороший ответ до сих пор.
сплитератор - правильно. упорядочено — проблема в том, что его можно заказать только в том случае, если оно конечно, иначе это займет вечность (например, Stream.generate(random::nextInt).sorted() вызовет предупреждение intellij), поэтому проверка на упорядоченность немного избыточна. Вместо того, чтобы держать счетчик и вкл. сами, почему бы не использовать limit(x) по максимуму?
Ограничение не говорит вам, что их было больше. Например, вызов Макса в очень длинном потоке, который может быть бесконечным, безопаснее генерировать исключение, чем возвращать неправильный номер.
Я не слишком уверен в этом - это очень зависит от вашего варианта использования, но я понимаю вашу точку зрения.
Если это подходящий ответ, не могли бы вы принять его?
Я думаю, многое зависит от этого. Подождал бы, пока кто-то вроде Хольгера ответит на этот вопрос, если не считать его широким.