Existe uma maneira concisa de iterar em um fluxo enquanto tiver acesso ao índice no fluxo?
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());
o que parece bastante decepcionante em comparação com o exemplo LINQ dado lá
string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();
Existe uma maneira mais concisa?
Além disso, parece que o zip foi movido ou removido ...
java
java-8
java-stream
Graeme Moss
fonte
fonte
intRange()
? Até agora, não encontramos esse método no Java 8.IntStream.rangeClosed(x, y)
.List<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
zip
foi removido, juntamente com fluxos experimentais de dois valores, chamados deBiStream
ouMapStream
. O principal problema é que, para fazer isso efetivamente, o Java realmente precisa de um tipo de par (ou tupla) de tipo estrutural. Na falta de uma, é fácil criar uma classe par ou tupla genérica - isso já foi feito várias vezes - mas todas elas são apagadas para o mesmo tipo.Respostas:
A maneira mais limpa é começar a partir de um fluxo de índices:
A lista resultante contém apenas "Erik".
Uma alternativa que parece mais familiar quando você está acostumado a fazer loops seria manter um contador ad hoc usando um objeto mutável, por exemplo
AtomicInteger
:Observe que o uso do último método em um fluxo paralelo pode ser interrompido, pois os itens não são necessariamente processados "em ordem" .
fonte
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
ousequential
é respeitado quando a operação do terminal começa.A API de fluxos Java 8 não possui os recursos para obter o índice de um elemento de fluxo, bem como a capacidade de compactar fluxos juntos. Isso é lamentável, pois torna certos aplicativos (como os desafios do LINQ) mais difíceis do que seriam.
No entanto, geralmente existem soluções alternativas. Geralmente, isso pode ser feito "direcionando" o fluxo com um intervalo inteiro e aproveitando o fato de que os elementos originais geralmente estão em uma matriz ou em uma coleção acessível por índice. Por exemplo, o problema do Desafio 2 pode ser resolvido desta maneira:
Como mencionei acima, isso tira proveito do fato de que a fonte de dados (a matriz de nomes) é diretamente indexável. Se não fosse, essa técnica não funcionaria.
Admito que isso não satisfaz a intenção do Desafio 2. No entanto, resolve o problema de maneira razoavelmente eficaz.
EDITAR
Meu exemplo de código anterior costumava
flatMap
fundir as operações de filtro e mapa, mas isso era complicado e não oferecia vantagem. Eu atualizei o exemplo pelo comentário de Holger.fonte
IntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])
? Ele faz o trabalho sem o boxe ...flatMap
assim mesmo?flatMap
porque meio que funde uma operação de filtragem e mapeamento em uma única operação, mas isso realmente não oferece nenhuma vantagem. Vou editar o exemplo.Stream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );
Menos remoção da caixa de seleção e menos alocação de memória; pois não estamos mais criando um fluxo de intervalo.Desde a goiaba 21, você pode usar
Exemplo (do documento oficial ):
fonte
Eu usei a seguinte solução no meu projeto. Eu acho que é melhor do que usar objetos mutáveis ou intervalos inteiros.
fonte
StreamSupport.stream()
e um iterador personalizado.Além do protonpack, o Seq do jOOλ fornece essa funcionalidade (e por bibliotecas de extensão que o compõem como cyclops-react , eu sou o autor desta biblioteca).
O Seq também suporta apenas o Seq.of (nomes) e criará um JDK Stream sob as cobertas.
O equivalente de reação simples seria semelhante a
A versão de reação simples é mais personalizada para processamento assíncrono / simultâneo.
fonte
Apenas para completar, aqui está a solução que envolve minha biblioteca StreamEx :
Aqui criamos um
EntryStream<Integer, String>
que estendeStream<Entry<Integer, String>>
e adiciona algumas operações específicas comofilterKeyValue
ouvalues
. TambémtoList()
é usado um atalho.fonte
.forEach(entry -> {})
?.forKeyValue((key, value) -> {})
.Encontrei as soluções aqui quando o Stream é criado de lista ou matriz (e você sabe o tamanho). Mas e se o Stream tiver tamanho desconhecido? Nesse caso, tente esta variante:
Uso:
fonte
Com uma lista, você pode tentar
Resultado:
fonte
Não há uma maneira de iterar durante um
Stream
tempo tendo acesso ao índice porque aStream
é diferente de qualquer outroCollection
. AStream
é apenas um pipeline para transportar dados de um lugar para outro, conforme declarado na documentação :Nenhum armazenamento. Um fluxo não é uma estrutura de dados que armazena elementos; em vez disso, eles carregam valores de uma fonte (que pode ser uma estrutura de dados, um gerador, um canal de IO etc.) através de um pipeline de operações computacionais.
Obviamente, como você parece sugerir na sua pergunta, você sempre pode convertê-lo
Stream<V>
em umCollection<V>
, como umList<V>
, no qual você terá acesso aos índices.fonte
Com https://github.com/poetix/protonpack, você pode fazer isso:
fonte
Se você não se importa em usar uma biblioteca de terceiros, o Eclipse Collections tem
zipWithIndex
e estáforEachWithIndex
disponível para uso em vários tipos. Aqui está um conjunto de soluções para esse desafio para os tipos JDK e Eclipse Collections usandozipWithIndex
.Aqui está uma solução usando em seu
forEachWithIndex
lugar.Se você alterar as lambdas para classes internas anônimas acima, todos esses exemplos de código também funcionarão no Java 5 - 7.
Nota: Eu sou um confirmador das Coleções Eclipse
fonte
Se você usar o Vavr (anteriormente conhecido como Javaslang), poderá aproveitar o método dedicado:
Se imprimirmos o conteúdo, veremos algo interessante:
Isso ocorre porque
Streams
são preguiçosos e não temos idéia dos próximos itens no fluxo.fonte
Se você está tentando obter um índice com base em um predicado, tente o seguinte:
Se você se importa apenas com o primeiro índice:
Ou se você deseja encontrar vários índices:
Adicione
.orElse(-1);
no caso de você querer retornar um valor se ele não o encontrar.fonte
Aqui está o código de AbacusUtil
Divulgação: sou o desenvolvedor do AbacusUtil.
fonte
Você pode usar
IntStream.iterate()
para obter o índice:Isso funciona apenas para o Java 9 para cima no Java 8, você pode usar isto:
fonte
Você pode criar uma classe interna estática para encapsular o indexador, como eu precisava fazer no exemplo abaixo:
fonte
Esta questão ( Stream Way para obter o índice do primeiro elemento correspondente a booleano ) marcou a pergunta atual como duplicada, por isso não posso respondê-la; Eu estou respondendo aqui.
Aqui está uma solução genérica para obter o índice correspondente que não requer uma biblioteca externa.
Se você tem uma lista.
E chame assim:
E se estiver usando uma coleção, tente esta.
fonte
Uma maneira possível é indexar cada elemento no fluxo:
O uso de uma classe anônima ao longo de um fluxo não é bem utilizado, sendo muito útil.
fonte
você não precisa
map
necessariamente de umlambda mais próximo do exemplo LINQ:
fonte
SAÍDA: Sam, Pamela, Dave, Pascal, Erik
Para coletar na lista:
fonte
List
um elemento contendo Erik .Como jean-baptiste-yunès disse, se o seu fluxo for baseado em uma lista java, usar um AtomicInteger e seu método incrementAndGet é uma solução muito boa para o problema e o número inteiro retornado corresponde ao índice na lista original, desde que você não use um fluxo paralelo.
fonte
Se você precisar do índice no forEach, isso fornece uma maneira.
Em seguida, use-o da seguinte maneira.
fonte