На многих других языках, например. Haskell, легко повторить значение или функцию несколько раз, например. чтобы получить список из 8 копий значения 1:
take 8 (repeat 1)
но я еще не нашел этого в Java 8. Есть ли такая функция в JDK Java 8?
Или, альтернативно, что-то эквивалентное диапазону вроде
[1..8]
Казалось бы, очевидная замена многословному выражению в Java вроде
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
иметь что-то вроде
Range.from(1, 8).forEach(i -> System.out.println(i))
хотя этот конкретный пример на самом деле не выглядит намного более сжатым ... но, надеюсь, он более читабелен.
Ответы:
В этом конкретном примере вы можете:
Если вам нужен шаг, отличный от 1, вы можете использовать функцию сопоставления, например, для шага 2:
Или создайте собственную итерацию и ограничьте размер итерации:
источник
IntStream.rangeClosed(1, 8).forEach(i -> methodNoArgs());
), но это сбивает с толку ИМО, и в этом случае цикл кажется указанным.Вот еще одна техника, с которой я столкнулся на днях:
Collections.nCopies
Вызов создаетList
содержащиеn
копии любое значение , вы предоставляете. В данном случае этоInteger
значение в рамке 1. Конечно, на самом деле он не создает список сn
элементами; он создает «виртуализированный» список, содержащий только значение и длину, и любой вызов вget
пределах диапазона просто возвращает значение. ЭтотnCopies
метод существует с тех пор, как Collections Framework был представлен еще в JDK 1.2. Конечно, в Java SE 8 была добавлена возможность создавать поток из его результата.Подумайте, еще один способ сделать то же самое примерно с таким же количеством строк.
Однако, этот метод быстрее
IntStream.generate
иIntStream.iterate
подходы, и удивительно, это также быстрее , чемIntStream.range
подход.Для
iterate
иgenerate
результат может быть , не слишком удивительно. Структура потоков (на самом деле, разделители для этих потоков) построена на предположении, что лямбда-выражения потенциально будут генерировать разные значения каждый раз и что они будут генерировать неограниченное количество результатов. Это особенно затрудняет параллельное разделение.iterate
Метод также проблематичен для этого случая , потому что каждый вызов требует результата предыдущей. Таким образом, потоки, использующиеgenerate
иiterate
не очень хорошо подходят для генерации повторяющихся констант.Относительно низкая производительность
range
вызывает удивление. Это тоже виртуализировано, поэтому на самом деле не все элементы существуют в памяти, а размер известен заранее. Это должно сделать сплитератор быстрым и легко распараллеливаемым. Но, на удивление, все получилось не очень хорошо. Возможно, причина в том, чтоrange
необходимо вычислить значение для каждого элемента диапазона, а затем вызвать для него функцию. Но эта функция просто игнорирует свой ввод и возвращает константу, поэтому я удивлен, что она не встроена и не убита.Collections.nCopies
Техника должна сделать бокс / распаковка для того , чтобы обрабатывать значения, так как нет примитивных специализацийList
. Поскольку значение каждый раз одно и то же , оно обычно упаковывается один раз, и это поле используется всемиn
копиями. Я подозреваю, что упаковка / распаковка сильно оптимизирована, даже встроена, и ее можно хорошо встроить.Вот код:
А вот результаты JMH: (Core2Duo 2,8 ГГц)
В версии ncopies есть немало различий, но в целом она кажется в 20 раз быстрее, чем версия диапазона. (Хотя мне бы хотелось поверить, что я сделал что-то не так.)
Я удивлен, насколько хорошо
nCopies
работает эта техника. Внутри он не делает ничего особенного, поток виртуализированного списка просто реализуется с помощьюIntStream.range
! Я ожидал, что потребуется создать специализированный сплитератор, чтобы все работало быстро, но это уже кажется неплохим.источник
nCopies
самом деле ничего не копирует, а все «копии» указывают на этот единственный объект. Всегда безопасно, если этот объект является неизменяемым , например, в этом примере примитив в коробке. Вы упоминаете об этом в своем заявлении «один раз в упаковке», но было бы неплохо явно указать здесь на предостережения, потому что такое поведение не является специфическим для автоматической упаковки.LongStream.range
это значительно медленнее, чемIntStream.range
? Так что хорошо, что идея не предлагатьIntStream
(а использоватьLongStream
для всех целочисленных типов) была отброшена. Обратите внимание, что для случая последовательного использования нет никакой причины использовать поток:Collections.nCopies(8, 1).forEach(i -> System.out.println(i));
делает то же самое,Collections.nCopies(8, 1).stream().forEach(i -> System.out.println(i));
но может быть даже более эффективнымCollections.<Runnable>nCopies(8, () -> System.out.println(1)).forEach(Runnable::run);
LongStream.range
работает хуже, потому что у него две карты сLongFunction
внутренним, аncopies
три карты сIntFunction
,ToLongFunction
иLongFunction
, таким образом, все лямбды мономорфны. Запуск этого теста на предварительно загрязненном профиле типа (который ближе к реальному случаю) показывает, чтоncopies
это в 1,5 раза медленнее.for
циклом. Хотя ваше решение быстрееStream
кода, я предполагаю, чтоfor
цикл значительно превзойдет любой из них.Для полноты, а также потому, что я ничего не мог с собой поделать :)
Генерация ограниченной последовательности констант довольно близка к тому, что вы видели бы в Haskell, только с детализацией уровня Java.
источник
() -> 1
будет генерировать только единицы, это предназначено? Так что на выходе будет1 1 1 1 1 1 1 1
.take 8 (repeat 1)
. assylias в значительной степени покрывает все остальные случаи.Stream<T>
также есть общийgenerate
метод получения бесконечного потока другого типа, который можно ограничить таким же образом.Как только функция повтора определена как
Вы можете использовать его время от времени таким образом, например:
Чтобы получить и эквивалент Haskell's
Вы могли написать
источник
Runnable
на,Function<Integer, ?>
а затем используяf.apply(i)
.Это мое решение для реализации функции времени. Я юниор, поэтому признаю, что это могло быть не идеально, я был бы рад услышать, если это не лучшая идея по какой-либо причине.
Вот несколько примеров использования:
источник