Como posso obter o último elemento de um fluxo ou lista no código a seguir?
Onde data.careas
está um List<CArea>
:
CArea first = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal).findFirst().get();
CArea last = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.collect(Collectors.toList()).; //how to?
Como você pode ver, pegar o primeiro elemento, com uma certa filter
, não é difícil.
No entanto, obter o último elemento em uma linha é uma verdadeira dor:
- Parece que não posso obtê-lo diretamente de a
Stream
. (Só faria sentido para fluxos finitos) - Também parece que você não pode obter coisas como
first()
elast()
daList
interface, o que é realmente uma dor.
Não vejo nenhum argumento para não fornecer um método first()
e last()
na List
interface, pois os elementos lá são ordenados e, além disso, o tamanho é conhecido.
Mas de acordo com a resposta original: Como obter o último elemento de um finito Stream
?
Pessoalmente, isso é o mais próximo que consegui:
int lastIndex = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);
No entanto, envolve o uso de um indexOf
em todos os elementos, o que geralmente você não deseja, pois pode prejudicar o desempenho.
java
list
java-8
java-stream
skiwi
fonte
fonte
Iterables.getLast
que leva Iterable, mas é otimizado para funcionarList
. Uma implicância é que ele não temgetFirst
. AStream
API em geral é terrivelmente anal, omitindo muitos métodos de conveniência. O LINQ do C #, por contraste, tem o prazer de fornecer.Last()
e até mesmo.Last(Func<T,Boolean> predicate)
, embora ofereça suporte a Enumerables infinitos também.Stream
A API não é totalmente comparável,LINQ
já que ambas são feitas em um paradigma muito diferente. Não é pior ou melhor, é apenas diferente. E, definitivamente, alguns métodos estão ausentes, não porque os desenvolvedores de oráculos sejam incompetentes ou mesquinhos :)Respostas:
É possível obter o último elemento com o método Stream :: reduce . A lista a seguir contém um exemplo mínimo para o caso geral:
Essas implementações funcionam para todos os fluxos ordenados (incluindo fluxos criados a partir de listas ). Para fluxos não ordenados , por razões óbvias não é especificado qual elemento será retornado.
A implementação funciona para fluxos sequenciais e paralelos . Isso pode ser surpreendente à primeira vista e, infelizmente, a documentação não afirma isso explicitamente. No entanto, é um recurso importante dos streams, e tento esclarecê-lo:
(first, second) -> second
.A documentação para os coletores intimamente relacionados é ainda mais explícita: "Para garantir que as execuções sequenciais e paralelas produzam resultados equivalentes , as funções do coletor devem satisfazer as restrições de identidade e associatividade ."
De volta à pergunta original: o código a seguir armazena uma referência ao último elemento na variável
last
e lança uma exceção se o fluxo estiver vazio. A complexidade é linear no comprimento do fluxo.fonte
_
ou semelhante) nos casos em que não precisa de um parâmetro? Então seria:.reduce((_, current) -> current)
se apenas essa sintaxe fosse válida..reduce(($, current) -> current)
ou.reduce((__, current) -> current)
(sublinhado duplo).Stream.reduce(BinaryOperator<T>)
não faz nenhuma menção sereduce
obedece a ordem de encontro, e uma operação de terminal é livre para ignorar a ordem de encontro, mesmo se o fluxo for ordenado. À parte, a palavra "comutativo" não aparece nos javadocs do Stream, portanto, sua ausência não nos diz muito.reduce((a,b)->b)
ser uma solução correta para obter o último elemento (de um fluxo ordenado, é claro) ou não. A declaração de Brian Goetz faz questão, além disso, a documentação da API afirma quereduce("", String::concat)
é uma solução ineficiente, mas correta para concatenação de strings, o que implica na manutenção da ordem do encontro. A intenção é bem conhecida, a documentação tem que ser atualizada.Se você tem uma coleção (ou mais um Iterable geral), pode usar o Google Guava's
como oneliner útil.
fonte
Iterables.getLast(() -> data.careas.stream().filter(c -> c.bbox.orientationHorizontal).iterator())
Um forro (sem necessidade de fluxo;):
fonte
ArrayIndexOutOfBoundsException
.Guava tem método dedicado para este caso:
É equivalente a,
stream.reduce((a, b) -> b)
mas os criadores afirmam que tem um desempenho muito melhor.Da documentação :
Vale a pena mencionar que se o stream estiver desordenado este método se comporta como
findAny()
.fonte
Se você precisar obter o último número N de elementos. O fechamento pode ser usado. O código abaixo mantém uma fila externa de tamanho fixo até que o fluxo chegue ao fim.
Outra opção poderia ser usar a operação de redução usando a identidade como uma fila.
fonte
Você também pode usar a função skip () conforme abaixo ...
é super simples de usar.
fonte
ArrayIndexOutOfBoundsException