Я могу добавить потоки или дополнительные элементы, например:
Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));
И я могу добавлять новые вещи, как я, как это:
Stream stream = Stream.concat(
Stream.concat(
stream1.filter(x -> x!=0), stream2)
.filter(x -> x!=1),
Stream.of(element))
.filter(x -> x!=2);
Но это безобразно, потому что concat
статично. Если бы concat
это был метод экземпляра, вышеприведенные примеры было бы намного легче читать:
Stream stream = stream1.concat(stream2).concat(element);
И
Stream stream = stream1
.filter(x -> x!=0)
.concat(stream2)
.filter(x -> x!=1)
.concat(element)
.filter(x -> x!=2);
Мой вопрос:
1) Есть ли веская причина, почему concat
статический? Или я пропускаю какой-то эквивалентный метод экземпляра?
2) В любом случае, есть ли лучший способ сделать это?
java
concat
java-8
java-stream
MarcG
источник
источник
Ответы:
Если вы добавите статический импорт для Stream.concat и Stream.of , первый пример может быть записан следующим образом:
Импорт статических методов с родовыми именами может привести к тому, что код станет трудным для чтения и обслуживания ( загрязнение пространства имен ). Поэтому может быть лучше создать свои собственные статические методы с более значимыми именами. Однако для демонстрации я буду придерживаться этого имени.
С помощью этих двух статических методов (возможно, в сочетании со статическим импортом) эти два примера можно записать следующим образом:
Код теперь значительно короче. Тем не менее, я согласен, что читаемость не улучшилась. Так что у меня есть другое решение.
Во многих ситуациях коллекторы могут использоваться для расширения функциональности потоков. С двумя коллекторами внизу два примера можно записать следующим образом:
Единственная разница между вашим желаемым синтаксисом и приведенным выше синтаксисом заключается в том, что вы должны заменить concat (...) на collect (concat (...)) . Два статических метода могут быть реализованы следующим образом (необязательно используется в сочетании со статическим импортом):
Конечно, у этого решения есть недостаток, о котором следует упомянуть. collect - это последняя операция, которая потребляет все элементы потока. Кроме того, concat сборщика создает промежуточный ArrayList каждый раз, когда он используется в цепочке. Обе операции могут оказать существенное влияние на поведение вашей программы. Однако, если удобочитаемость важнее производительности , это может быть очень полезным подходом.
источник
concat
коллекционера очень читабельным. Кажется странным иметь статический метод с одним параметром, который называется так, а также использоватьcollect
для объединения.It's a bad idea to import static methods with names
? Я искренне заинтересован - я считаю, что это делает код более кратким и читабельным, и многие люди, которых я спрашивал, думали так же. Не забудьте привести несколько примеров, почему это вообще плохо?compare(reverse(getType(42)), of(6 * 9).hashCode())
? Обратите внимание, что я не говорил, что статический импорт - плохая идея, но статический импорт для общих имен, таких какof
иconcat
есть.К сожалению, этот ответ, вероятно, мало или вообще не поможет, но я провел судебный анализ списка рассылки Java Lambda, чтобы посмотреть, смогу ли я найти причину этого дизайна. Это то, что я узнал.
В начале был экземпляр метода для Stream.concat (Stream)
В списке рассылки я ясно вижу, что метод изначально был реализован как метод экземпляра, как вы можете прочитать в этой теме Пола Сандоса об операции concat.
В нем они обсуждают проблемы, которые могут возникнуть в тех случаях, когда поток может быть бесконечным, и что будет означать конкатенация в этих случаях, но я не думаю, что это было причиной для модификации.
В этом другом потоке вы видите, что некоторые ранние пользователи JDK 8 спрашивали о поведении метода экземпляра concat при использовании с нулевыми аргументами.
Этот другой поток показывает, однако, что проект метода concat обсуждался.
Преобразуется в Streams.concat (Stream, Stream)
Но без какого-либо объяснения, внезапно, методы были изменены на статические методы, как вы можете видеть в этой теме об объединении потоков . Это, пожалуй, единственная почтовая рассылка, которая проливает немного света на это изменение, но мне было недостаточно ясно определить причину рефакторинга. Но мы можем видеть, что они сделали коммит, в котором они предложили переместить
concat
метод изStream
класса помощника в класс помощника.Streams
.Преобразуется в Stream.concat (Stream, Stream)
Позже, он был снова перемещен из
Streams
вStream
, но опять же, без объяснения этого.Итак, суть в том, что причина дизайна мне не совсем понятна, и я не смог найти хорошего объяснения. Я думаю, вы все еще можете задать вопрос в списке рассылки.
Некоторые альтернативы для объединения потоков
Этот другой поток Майкла Хиксона обсуждает / спрашивает о других способах объединения / объединения потоков
источник
public static <T> Stream<T> concat(Stream<T>... streams) { return Stream.of(streams).reduce(Stream.empty(), Stream::concat);}
@SafeVarargs private static <T> Stream<T> concat(Stream<? extends T>... streams) { return Stream.of(streams).reduce(Stream.empty(),Stream::concat).map(Function.identity());}
Function.identity()
карта? В конце концов, он возвращает тот же аргумент, который получает. Это не должно иметь никакого эффекта в результирующем потоке. Я что-то упускаю?return Stream.of(streams).reduce(Stream.empty(),Stream::concat)
возвращает Stream <? extends T>. (Someting <T> является подтипом Something <? extends T>, а не другим способом, поэтому его нельзя привести). Дополнительное.map(identity())
приведение <? расширяет T> до <T>. Это происходит благодаря сочетанию java 8 «целевых типов» аргументов метода и возвращаемых типов и сигнатуры метода map (). На самом деле это Function. <T> identity ().? extends T
, так как вы можете использовать преобразование захвата . В любом случае, вот мой фрагмент кода гисти. Давайте продолжим обсуждение в гисте.Моя библиотека StreamEx расширяет функциональность Stream API. В частности, он предлагает методы, такие как append и prepend, которые решают эту проблему (внутренне они используют
concat
). Эти методы могут принимать либо другой поток, либо коллекцию, либо массив varargs. Используя мою библиотеку, ваша проблема может быть решена следующим образом (обратите внимание, чтоx != 0
для непримитивного потока это выглядит странно):Кстати, есть также ярлык для вашей
filter
операции:источник
Просто сделать:
где
identity()
статический импортFunction.identity()
.Объединение нескольких потоков в один поток аналогично выравниванию потока.
Однако, к сожалению, по какой-то причине
flatten()
метод не включенStream
, поэтому вы должны использоватьflatMap()
функцию идентификации.источник
Вы можете использовать метод Guava , который будет очень коротким при статическом импорте:
Streams
.
concat(Stream<? extends T>... streams)
источник
Если вы не возражаете против использования сторонних библиотек, у cyclops-реагирует расширенный тип потока, который позволит вам сделать это с помощью операторов добавления / добавления.
Отдельные значения, массивы, итерации, потоки или реактивные потоки Издатели могут добавляться и добавляться в качестве методов экземпляра.
[Раскрытие Я ведущий разработчик циклоп-реакции]
источник
В конце концов я не заинтересован в объединении потоков, а в получении объединенного результата обработки каждого элемента всех этих потоков.
Хотя объединение потоков может оказаться громоздким (таким образом, этот поток), объединение их результатов обработки довольно легко.
Ключом к решению является создание собственного сборщика и обеспечение того, чтобы функция поставщика для нового сборщика каждый раз возвращала одну и ту же коллекцию (а не новую ). Этот код иллюстрирует приведенный ниже код.
источник
Как насчет написания собственного метода concat?
По крайней мере, это делает ваш первый пример намного более читабельным.
источник