Comment puis-je obtenir le dernier élément d'un flux ou d'une liste dans le code suivant?
Où data.careas
est un 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?
Comme vous pouvez le voir, obtenir le premier élément, avec un certain filter
, n'est pas difficile.
Cependant, obtenir le dernier élément d'un one-liner est une vraie douleur:
- Il semble que je ne puisse pas l'obtenir directement à partir d'un fichier
Stream
. (Cela n'aurait de sens que pour les flux finis) - Il semble également que vous ne pouvez pas obtenir des choses comme
first()
et àlast()
partir de l'List
interface, ce qui est vraiment pénible.
Je ne vois aucun argument pour ne pas fournir une méthode first()
and last()
dans l' List
interface, car les éléments y sont ordonnés et de plus la taille est connue.
Mais selon la réponse originale: comment obtenir le dernier élément d'un fini Stream
?
Personnellement, c'est le plus proche que je puisse obtenir:
int lastIndex = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);
Cependant, cela implique l'utilisation d'un indexOf
élément sur chaque élément, ce qui n'est probablement pas souhaité car cela peut nuire aux performances.
la source
Iterables.getLast
ce qui prend Iterable mais est optimisé pour fonctionner avecList
. Une bête noire, c'est qu'il n'en a pasgetFirst
. L'Stream
API en général est horriblement anale, omettant de nombreuses méthodes pratiques. LINQ de C #, par contraste, est heureux de fournir.Last()
et même.Last(Func<T,Boolean> predicate)
, même s'il prend également en charge des Enumerables infinis.Stream
L'API n'est pas entièrement comparableLINQ
car les deux sont réalisés dans un paradigme très différent. Ce n'est ni pire ni meilleur, c'est juste différent. Et certainement certaines méthodes sont absentes non pas parce que les développeurs oracle sont incompétents ou méchants :)Réponses:
Il est possible d'obtenir le dernier élément avec la méthode Stream :: reduction . La liste suivante contient un exemple minimal pour le cas général:
Cette implémentation fonctionne pour tous les flux ordonnés (y compris les flux créés à partir de listes ). Pour non ordonné flux , c'est pour des raisons évidentes non spécifiées quel élément sera retourné.
L'implémentation fonctionne pour les flux séquentiels et parallèles . Cela peut être surprenant à première vue, et malheureusement la documentation ne l'indique pas explicitement. Cependant, c'est une caractéristique importante des flux, et j'essaye de la clarifier:
(first, second) -> second
.La documentation des Collecteurs étroitement liés est encore plus explicite: "Pour s'assurer que les exécutions séquentielles et parallèles produisent des résultats équivalents , les fonctions de collecteur doivent satisfaire une contrainte d' identité et d' associativité ."
Retour à la question d'origine: Le code suivant stocke une référence au dernier élément de la variable
last
et lève une exception si le flux est vide. La complexité est linéaire dans la longueur du flux.la source
_
ou similaire) dans les cas où vous n'avez pas besoin d'un paramètre? Ce serait le cas:.reduce((_, current) -> current)
si seulement cette syntaxe était valide..reduce(($, current) -> current)
ou.reduce((__, current) -> current)
(double tiret bas).Stream.reduce(BinaryOperator<T>)
ne fait aucune mention sireduce
obéit à l'ordre de rencontre, et une opération de terminal est libre d'ignorer l'ordre de rencontre même si le flux est commandé. En passant, le mot «commutatif» n'apparaît pas dans les javadocs Stream, donc son absence ne nous en dit pas beaucoup.reduce((a,b)->b)
être une solution correcte pour obtenir le dernier élément (d'un flux ordonné, bien sûr) ou non. La déclaration de Brian Goetz fait un point, en outre la documentation de l' API indique quereduce("", String::concat)
c'est une solution inefficace mais correcte pour la concaténation de chaînes, ce qui implique le maintien de l'ordre des rencontres. L'intention est bien connue, la documentation doit rattraper son retard.Si vous avez une collection (ou plus généralement un Iterable), vous pouvez utiliser Google Guava
comme oneliner pratique.
la source
Iterables.getLast(() -> data.careas.stream().filter(c -> c.bbox.orientationHorizontal).iterator())
Une doublure (pas besoin de flux;):
la source
ArrayIndexOutOfBoundsException
.Guava a une méthode dédiée pour ce cas:
C'est équivalent à
stream.reduce((a, b) -> b)
mais les créateurs affirment que ses performances sont bien meilleures.De la documentation :
Il convient de mentionner que si le flux n'est pas ordonné, cette méthode se comporte comme
findAny()
.la source
Si vous avez besoin d'obtenir le dernier nombre d'éléments N. La fermeture peut être utilisée. Le code ci-dessous maintient une file d'attente externe de taille fixe jusqu'à ce que le flux atteigne la fin.
Une autre option pourrait être d'utiliser une opération de réduction en utilisant l'identité comme file d'attente.
la source
Vous pouvez également utiliser la fonction skip () comme ci-dessous ...
c'est super simple à utiliser.
la source
ArrayIndexOutOfBoundsException