Есть ли краткий способ перебора потока, имея доступ к индексу в потоке?
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey())
.map(Entry::getValue)
.collect(toList());
что выглядит довольно обидно по сравнению с приведенным там примером LINQ
string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();
Есть ли более лаконичный способ?
Далее кажется, что почтовый индекс либо сдвинулся, либо был удален ...
java
java-8
java-stream
Грем Мосс
источник
источник
intRange()
? До сих пор не встречал этот метод в Java 8.IntStream.rangeClosed(x, y)
.List<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
zip
был удален наряду с экспериментальными двузначными потоками, называемыми по-разномуBiStream
илиMapStream
. Основная проблема заключается в том, что для эффективного выполнения Java действительно необходим тип структурной пары (или кортежа). Без него легко создать общий класс Pair или Tuple - это было сделано много раз - но все они стираются до одного и того же типа.Ответы:
Самый чистый способ - начать с потока индексов:
Полученный список содержит только «Эрик».
Одна из альтернатив, которая выглядит более знакомой, когда вы привыкли к циклам for, - это поддерживать счетчик ad hoc, используя изменяемый объект, например
AtomicInteger
:Обратите внимание, что использование последнего метода в параллельном потоке может прерваться, поскольку элементы не будут обязательно обрабатываться «по порядку» .
источник
public static <T> Stream<Tuple2<Integer, T>> zipWithIndex(Stream<T> stream) { final AtomicInteger index = new AtomicInteger(); final Function<T, Tuple2<Integer, T>> zipper = e -> Tuples.of(index.getAndIncrement(), e); if (stream.isParallel()) { return stream.sequential().map(zipper).parallel(); } else { return stream.map(zipper); } }
parallel
илиsequential
учитывается, когда начинается работа терминала.В API потоков Java 8 отсутствуют функции получения индекса элемента потока, а также возможность объединения потоков. Это прискорбно, поскольку делает некоторые приложения (например, задачи LINQ) более сложными, чем они были бы в противном случае.
Однако часто есть обходные пути. Обычно это можно сделать, "управляя" потоком с целочисленным диапазоном, и используя тот факт, что исходные элементы часто находятся в массиве или в коллекции, доступной по индексу. Например, проблема Challenge 2 может быть решена следующим образом:
Как я упоминал выше, это использует тот факт, что источник данных (массив имен) может быть непосредственно проиндексирован. Если бы не было, эта техника не сработала бы.
Я признаю, что это не удовлетворяет цели Задачи 2. Тем не менее, это действительно решает проблему достаточно эффективно.
РЕДАКТИРОВАТЬ
Мой предыдущий пример кода использовался
flatMap
для объединения операций фильтрации и отображения, но это было громоздко и не давало никаких преимуществ. Я обновил пример согласно комментарию от Хольгера.источник
IntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])
? Это работает без бокса ...flatMap
любом случае?flatMap
потому что он как бы объединяет операцию фильтрации и отображения в одну операцию, но это действительно не дает никаких преимуществ. Я отредактирую пример.Stream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );
меньше распаковки и меньше выделения памяти; поскольку мы больше не создаем поток диапазона.Начиная с гуавы 21, вы можете использовать
Пример (из официального документа ):
источник
Я использовал следующее решение в моем проекте. Я думаю, что это лучше, чем использование изменяемых объектов или целочисленных диапазонов.
источник
StreamSupport.stream()
и пользовательский итератор.В дополнение к protonpack, Seq в jOOλs предоставляет эту функциональность (и благодаря библиотекам расширений, которые основаны на нем, как cyclops-реагировать , я являюсь автором этой библиотеки).
Seq также поддерживает только Seq.of (names) и будет создавать поток JDK под прикрытиями.
Эквивалент простой реакции будет выглядеть так же, как
Версия с простым реагированием более приспособлена для асинхронной / параллельной обработки.
источник
Просто для полноты вот решение с использованием моей библиотеки StreamEx :
Здесь мы создаем
EntryStream<Integer, String>
который расширяетStream<Entry<Integer, String>>
и добавляет некоторые конкретные операции, такие какfilterKeyValue
илиvalues
. ТакжеtoList()
используется ярлык.источник
.forEach(entry -> {})
?.forKeyValue((key, value) -> {})
.Я нашел решения здесь, когда поток создается из списка или массива (и вы знаете размер). Но что, если Stream имеет неизвестный размер? В этом случае попробуйте этот вариант:
Применение:
источник
С помощью списка вы можете попробовать
Вывод:
источник
Невозможно перебрать
Stream
, имея доступ к индексу, потому что онStream
не похож ни на одинCollection
. АStream
- это просто конвейер для переноса данных из одного места в другое, как указано в документации :Нет хранения Поток - это не структура данных, в которой хранятся элементы; вместо этого они переносят значения из источника (который может быть структурой данных, генератором, каналом ввода-вывода и т. д.) через конвейер вычислительных операций.
Конечно, как вы, похоже, намекаете в своем вопросе, вы всегда можете преобразовать свое значение
Stream<V>
в aCollection<V>
, такое как aList<V>
, в котором у вас будет доступ к индексам.источник
С https://github.com/poetix/protonpack вы можете сделать это zip:
источник
Если вы не возражаете против использования сторонней библиотеки, Eclipse Collections имеет
zipWithIndex
иforEachWithIndex
доступен для использования во многих типах. Вот набор решений этой проблемы для типов JDK и коллекций EclipsezipWithIndex
.Вот решение, использующее
forEachWithIndex
вместо этого.Если вы измените лямбда-выражения на анонимные внутренние классы выше, тогда все эти примеры кода будут работать и в Java 5-7.
Примечание: я являюсь коммиттером для коллекций Eclipse
источник
Если вы используете Vavr (ранее известный как Javaslang), вы можете использовать выделенный метод:
Если мы распечатаем контент, мы увидим что-то интересное:
Это потому
Streams
, что ленивы, и мы понятия не имеем о следующих элементах в потоке.источник
Если вы пытаетесь получить индекс, основанный на предикате, попробуйте это:
Если вам важен только первый индекс:
Или, если вы хотите найти несколько индексов:
Добавьте,
.orElse(-1);
если вы хотите вернуть значение, если оно не найдено.источник
Вот код от AbacusUtil
Раскрытие информации: я разработчик AbacusUtil.
источник
Вы можете использовать
IntStream.iterate()
для получения индекса:Это работает только для Java 9 и выше в Java 8, вы можете использовать это:
источник
Вы можете создать статический внутренний класс для инкапсуляции индексатора, как мне нужно было сделать в примере ниже:
источник
Этот вопрос ( Stream Way для получения индекса первого элемента, совпадающего с логическим значением ) пометил текущий вопрос как дубликат, поэтому я не могу ответить на него там; Я отвечаю на это здесь.
Вот общее решение для получения соответствующего индекса, который не требует внешней библиотеки.
Если у вас есть список.
И назовите это так:
И если вы используете коллекцию, попробуйте этот.
источник
Одним из возможных способов является индексирование каждого элемента в потоке:
Использование анонимного класса в потоке не очень полезно, хотя и очень полезно.
источник
вам не нужно
map
обязательно,что является ближайшей лямбда к примеру LINQ:
источник
ВЫХОД: Сэм, Памела, Дейв, Паскаль, Эрик
Собрать в списке:
источник
List
содержать один элемент Эрик .Как сказал jean-baptiste-yunès, если ваш поток основан на java List, то использование AtomicInteger и его метода incrementAndGet является очень хорошим решением проблемы, и возвращаемое целое число соответствует индексу в исходном List, если вы не используйте параллельный поток.
источник
Если вам нужен индекс в forEach, то это обеспечивает способ.
Затем используйте его следующим образом.
источник