Jak mogę uzyskać ostatni element strumienia lub listy w poniższym kodzie?
Gdzie data.careas
jest 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?
Jak widać zdobycie pierwszego elementu, z pewnym filter
, nie jest trudne.
Jednak uzyskanie ostatniego elementu w jednolinijce to prawdziwy ból:
- Wygląda na to, że nie mogę go uzyskać bezpośrednio z pliku
Stream
. (Miałoby to sens tylko w przypadku skończonych strumieni) - Wydaje się również, że nie można dostać rzeczy, jak
first()
ilast()
zList
interfejsem, który jest naprawdę uciążliwe.
Nie widzę żadnego argumentu za brakiem podania metody first()
a last()
w List
interfejsie, ponieważ elementy tam są uporządkowane, a ponadto znany jest rozmiar.
Ale zgodnie z pierwotną odpowiedzią: jak uzyskać ostatni element skończonego Stream
?
Osobiście jest to najbliższe, jakie mogłem uzyskać:
int lastIndex = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);
Jednak wymaga to użycia indexOf
na każdym elemencie, co najprawdopodobniej nie jest ogólnie pożądane, ponieważ może to pogorszyć wydajność.
java
list
java-8
java-stream
skiwi
źródło
źródło
Iterables.getLast
który używa Iterable, ale jest zoptymalizowany do pracy zList
. Irytacją jest to, że nie magetFirst
. OgólnieStream
API jest strasznie analne, pomijając wiele wygodnych metod. C # LINQ, przez constrast, z przyjemnością zapewnia,.Last()
a nawet.Last(Func<T,Boolean> predicate)
, mimo że obsługuje również nieskończone Enumerables.Stream
API nie jest w pełni porównywalne,LINQ
ponieważ oba są wykonywane w zupełnie innym paradygmacie. Nie jest gorzej ani lepiej, jest po prostu inaczej. I zdecydowanie brakuje niektórych metod nie dlatego, żeOdpowiedzi:
Ostatni element można pobrać metodą Stream :: Redukcja . Poniższa lista zawiera minimalny przykład ogólnego przypadku:
Ta implementacja działa dla wszystkich uporządkowanych strumieni (w tym strumieni utworzonych z list ). W przypadku strumieni nieuporządkowanych z oczywistych powodów nie określono, który element zostanie zwrócony.
Implementacja działa zarówno dla strumieni sekwencyjnych, jak i równoległych . Na pierwszy rzut oka może to być zaskakujące i niestety dokumentacja nie podaje tego wprost. Jest to jednak ważna cecha strumieni i staram się to wyjaśnić:
(first, second) -> second
.Dokumentacja dla blisko spokrewnionych Kolektorów jest jeszcze bardziej wyraźna: „Aby zapewnić, że wykonywanie sekwencyjne i równoległe daje równoważne wyniki , funkcje kolektora muszą spełniać ograniczenia dotyczące tożsamości i asocjatywności ”.
Wracając do pierwotnego pytania: poniższy kod przechowuje odwołanie do ostatniego elementu zmiennej
last
i zgłasza wyjątek, jeśli strumień jest pusty. Złożoność jest liniowa w długości strumienia.źródło
_
lub podobnego) w przypadkach, gdy nie potrzebujesz parametru? Tak by było:.reduce((_, current) -> current)
gdyby tylko taka składnia aws była prawidłowa..reduce(($, current) -> current)
lub.reduce((__, current) -> current)
(podwójne podkreślenie).Stream.reduce(BinaryOperator<T>)
nie wspomina o tym, czyreduce
przestrzega rozkazu napotkania, a operacja terminalowa może zignorować kolejność spotkań, nawet jeśli strumień jest uporządkowany. Nawiasem mówiąc, słowo „przemienność” nie pojawia się w javadocach Stream, więc jego brak niewiele nam mówi.reduce((a,b)->b)
byciem poprawnym rozwiązaniem dla uzyskania ostatniego elementu (oczywiście z uporządkowanego strumienia), czy nie. Oświadczenie Briana Goetza mówi o tym, dalej dokumentacja API stwierdza, żereduce("", String::concat)
jest to nieefektywne, ale poprawne rozwiązanie dla konkatenacji ciągów, co oznacza utrzymanie kolejności spotkań. Zamiar jest dobrze znany, dokumentacja musi nadrobić zaległości.Jeśli masz kolekcję (lub bardziej ogólnie, iterowalną), możesz użyć Google Guava
jako poręczny oneliner.
źródło
Iterables.getLast(() -> data.careas.stream().filter(c -> c.bbox.orientationHorizontal).iterator())
Jeden liner (nie ma potrzeby streamowania;):
źródło
ArrayIndexOutOfBoundsException
.Guava ma dedykowaną metodę dla tego przypadku:
Jest to odpowiednik,
stream.reduce((a, b) -> b)
ale twórcy twierdzą, że ma znacznie lepszą wydajność.Z dokumentacji :
Warto wspomnieć, że jeśli strumień jest nieuporządkowany ta metoda zachowuje się jak
findAny()
.źródło
Jeśli chcesz uzyskać ostatnią liczbę N elementów. Można użyć zamknięcia. Poniższy kod utrzymuje zewnętrzną kolejkę o stałym rozmiarze do momentu, gdy strumień osiągnie koniec.
Inną opcją może być użycie operacji redukcji przy użyciu tożsamości jako kolejki.
źródło
Możesz także użyć funkcji skip () jak poniżej ...
jest bardzo prosty w użyciu.
źródło
ArrayIndexOutOfBoundsException