Я знаю, что существует Stream.concat (док) для объединения двух потоков. Однако я сталкивался со случаями, когда мне нужно добавить «еще несколько» элементов в существующий поток, а затем продолжить его обработку. В такой ситуации я ожидал, что смогу объединить такие методы, как:
getStream(someArg)
.map(Arg::getFoo)
.concat(someOtherStreamOfFoos) // Or append, or...
.map(...)
Однако такого метода цепочки append/concat на уровне экземпляра не существует.
Это не вопрос, требующий решения этой проблемы или более элегантных подходов (хотя я, конечно, был бы благодарен за любые другие точки зрения!). Скорее, я спрашиваю о факторах дизайна, которые привели к этому решению. Интерфейс Stream, как я полагаю, был разработан некоторыми чрезвычайно умными людьми, которые знают о Принцип наименьшего удивления, поэтому я должен предположить, что их решение опустить этот (для меня) интуитивно очевидный метод означает либо то, что метод является антипаттерном, либо что это невозможно из-за некоторых технических ограничений. Я бы хотел узнать причину.
Я подозреваю, что это не сработало бы с системой типов.
@ Aominè - согласен, практически такая же функциональность определенно по-прежнему доступна существующими методами. Мой аргумент больше связан с удобочитаемостью - «чтение вниз» конвейера Stream с последовательными операциями фильтрации / сопоставления / сокращения для меня гораздо более интуитивно понятно, чем необходимость «перепрыгивать» на несколько строк (после того, как я заметил лишнее закрытие) чтобы идентифицировать concat, который был заявлен выше.
Хороший вопрос. Возможно, комментарий Use caution when constructing streams from repeated concatenation является его частью. Если бы это был беглый API, у вас может возникнуть соблазн многократно объединять, но вынуждаете вас это делать, например. concat(s1, concat(s2, s3)) делает людей менее склонными к этому?
Я нашел набор изменений, который перемещает concat в статический метод: mail.openjdk.java.net/pipermail/lambda-dev/2012-November/…, но никаких комментариев относительно того, почему, похоже, не дано. Ни здесь: bugs.openjdk.java.net/browse/JDK-8015315
Я полагаю, что реализация конвейера Stream достаточно сложна, чтобы позволить использовать более одного источника для элементов. Я также не знаю, как параллелизм будет играть в этой ситуации. В библиотеке StreamEx есть методы append и prepend, так что это выполнимо. Однако я не осознаю сложностей.




Я могу назвать вам одну причину, по которой это не сработало бы.
Stream.concat определяется как
static <T> Stream<T> concat(Stream<? extends T> a,
Stream<? extends T> b)
Вы можете concat, Stream<HashMap> и Stream<Map> в Stream<Map> или даже concat, Stream<HashMap> и Stream<TreeMap> в Stream<Map>. Чтобы сделать это с помощью метода экземпляра, вам нужно будет объявить параметр типа, например <U super T>, что в Java не разрешено.
// It'd look kind of like this, if Java allowed it.
public <U super T> Stream<U> concat(Stream<? extends U> other)
Java допускает только параметры типа с ограничением сверху, но не с ограничением снизу.
Объединение Stream<Something> и Stream<SomethingElse> может показаться необычным, но вывод типа часто дает параметры типа, слишком специфичные для работы с методом экземпляра. Например,
Stream.concat(Stream.of(dog), animalStream)
который потребовал бы явного параметра типа, если бы он был записан как
Stream.<Animal>of(dog).concat(animalStream)
Я думаю, что возвращение одного и того же универсального типа все равно будет полезно в большинстве случаев, например, когда два потока имеют один и тот же тип. В редких случаях они могут оставить менее удобную статическую версию. Кроме того, для тех, кому интересно, что Зачем<U super T> недействителен Java, см .: stackoverflow.com/a/33895470/2643425
Если я не ошибаюсь, это возможно с помощью метода цепочки, описанного в bugs.openjdk.java.net/browse/JDK-8140283? Тогда это уменьшило бы проблему читабельности.
Это правда, но на практике это не имело бы значения. Если бы подпись была Stream<T> concat(Stream<? extends T> other), это было бы нормально для большинства случаев. В редких случаях, когда вы действительно хотите объединить Stream<Integer> и Stream<Double>, вы могли бы просто сделать integerStream.map(a -> (Number) a).concat(doubleStream).
@PaulBoddington: Я подозреваю, что это будет иметь большее значение в случаях, когда первый аргумент также требует вывода типа, например Stream.concat(Stream.of(dog), animalStream).
Думаю, это просто упущенная функциональность Stream API.
Обратите внимание, что в Observable RxJava есть метод concatWith с требуемой функциональностью, поэтому ваш вопрос является разумным:
Observable<String> o1 = ...;
Observable<Integer> o2 = ...;
o1.map(s -> s.length())
.concatWith(o2)
....
У Java 8 есть еще одна функциональность, которую приятно иметь - получить еще один необязательный, если текущий необязательный параметр пуст, например:
Optional.ofNullable(x).orElse(anotherOptional)
Я хочу сказать, что описанный вами concat возможен, но не реализован в Stream.
использование orElse(anotherOptional) неправильно. Либо это должен быть orElse(aValue), либо or(anotherOptional), представленный java9.
Это хороший вопрос, но я пытаюсь придумать случай, когда сделать его методом экземпляра лучше, чем быть
static. например, показанный вами фрагмент кода также может быть записан какStream.concat(getStream(someArg).map(Arg::getFoo), someOtherStreamOfFoos).map(...)