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 constater, obtenir le premier élément, avec un certain filter
, n’est pas difficile.
Cependant, obtenir le dernier élément dans une ligne est une vraie douleur:
Stream
. (Cela n'aurait de sens que pour les flux finis)first()
et last()
à partir de l'interface List
, ce qui est vraiment pénible.Je ne vois aucun argument pour ne pas fournir de méthode first()
et last()
dans l'interface List
, car les éléments qu'il contient sont ordonnés et, en outre, la taille est connu.
Mais comme dans la réponse initiale: Comment obtenir le dernier élément d’un Stream
fini?
Personnellement, c'est le plus proche que j'ai pu 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
sur chaque élément, ce qui n'est probablement pas ce que vous voulez en général, car cela pourrait nuire aux performances.
Il est possible d’obtenir le dernier élément avec la méthode Stream :: réduire . La liste suivante contient un exemple minimal pour le cas général:
Stream<T> stream = ...; // sequential or parallel stream
Optional<T> last = stream.reduce((first, second) -> second);
Cette implémentation fonctionne pour tous les flux ordonnés (y compris les flux créés à partir de Listes ). Pour les flux non ordonnés , il est impossible de spécifier quel élément sera retourné pour des raisons évidentes.
L'implémentation fonctionne pour les flux parallèles séquentiels et . Cela peut paraître surprenant à première vue, et malheureusement la documentation ne l’indique pas explicitement. Cependant, il s’agit d’une caractéristique importante des flux, et j’essaie de le préciser:
(first, second) -> second
.La documentation pour le proche parent Collectors est encore plus explicite: "Pour assurer que séquentiel et Les exécutions parallèles produisent des résultats équivalents , les fonctions de collecteur doivent satisfaire une identité et un - associativité contraintes. "
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.
CArea last = data.careas
.stream()
.filter(c -> c.bbox.orientationHorizontal)
.reduce((first, second) -> second).get();
Si vous avez une collection (ou plus générale une Iterable), vous pouvez utiliser Google Guava
Iterables.getLast(myIterable)
comme oneliner pratique.
Une doublure (pas besoin de flux;):
Object lastElement = list.get(list.size()-1);
Guava a dédié une méthode pour ce cas:
Stream<T> stream = ...;
Optional<T> lastItem = Streams.findLast(stream);
Cela équivaut à stream.reduce((a, b) -> b)
, mais les créateurs prétendent que ses performances sont bien meilleures.
De documentation :
Le temps d'exécution de cette méthode se situera entre O (log n) et O (n), ce qui donnera de meilleurs résultats sur les flux pouvant être fractionnés efficacement.
Il vaut la peine de mentionner que si le flux n'est pas ordonné, cette méthode se comporte comme findAny()
.