Java: zdobądź pierwszy przedmiot z kolekcji

277

Jeśli mam kolekcję, np. Collection<String> strsJak mogę uzyskać pierwszy przedmiot? Mógłbym po prostu zadzwonić Iterator, wziąć pierwszy next(), a potem wyrzucić Iterator. Czy istnieje mniej marnotrawny sposób?

Nick Heiner
źródło
1
Oczywiście może istnieć lepszy sposób na uzyskanie dostępu do pierwszego elementu, jeśli znasz implementującą klasę kontenera ...
Rooke,
Uogólnienie dla dowolnego indeksu: stackoverflow.com/questions/1047957/…
Ciro Santilli 23 冠状 病 六四 事件 法轮功 法轮功
1
Wygląda na to, że potrzebujesz Queue.peek ()
Johannes

Odpowiedzi:

131

Iterables.get (yourC, indexYouWant)

Ponieważ tak naprawdę, jeśli używasz kolekcji, powinieneś używać kolekcji Google.

Carl
źródło
7
To samo robi, po prostu sprawdza, czy najpierw jest to lista, i pobiera według indeksu, jeśli tak jest. Zawiera również kod, który próbuje szybciej zawieść w rzeczywistej kolekcji (to znaczy, jeśli indeks jest zbyt duży, próbuje to rozgryźć bez iteracji przez całą sprawę i odrzucenia wyjątku na końcu).
Yishai,
1
Szczerze mówiąc, pod względem wydajności może być nieco wolniejszy niż c.iterator (). Next () - ale kod jest znacznie wyraźniejszy i łatwiejszy do modyfikacji.
Carl
2
Z pewnością zgadzam się, że jest czystszy, ale OP dotyczyło marnotrawstwa, ale myślę, że skoro Twoja odpowiedź została zaakceptowana, to było to, czego pożądano.
Yishai,
8
Dla tych (wciąż) przybywających tutaj: myślę, że odpowiedź Jheddingsa jest prawdopodobnie najlepszą odpowiedzią „zrób to”, chociaż wolę odpowiedź @ DonaldRaab (w dół strony) dla przypadków, w których już korzystam z biblioteki GC. Moja odpowiedź jest naprawdę w przypadku, gdy ktoś może chcieć pisać elastycznie na później (powiedzmy, jeśli zdecyduje się, że drugim elementem jest nowa gorliwość).
Carl
4
Czasami używasz tylko kodu korzystającego z kolekcji, więc nie ma wiele do zrobienia.
erickrf,
436

Wygląda na to, że jest to najlepszy sposób:

String first = strs.iterator().next();

Świetne pytanie ... Na początku wydaje się to niedopatrzeniem Collectioninterfejsu.

Pamiętaj, że „pierwsze” nie zawsze zwróci pierwszą rzecz, którą umieścisz w kolekcji, i może mieć sens tylko w przypadku zamówionych kolekcji. Być może dlatego nie ma get(item)połączenia, ponieważ kolejność niekoniecznie jest zachowana.

Choć może wydawać się to trochę marnotrawstwem, może nie być tak złe, jak myślisz. IteratorNaprawdę tylko zawiera informacje indeksowania do kolekcji, a nie zwykle kopię całej kolekcji. Wywołanie tej metody tworzy instancję Iteratorobiektu, ale tak naprawdę jest to jedyny narzut (nie tak jak kopiowanie wszystkich elementów).

Na przykład, patrząc na typ zwracany przez ArrayList<String>.iterator()metodę, widzimy, że tak jest ArrayList::Itr. Jest to klasa wewnętrzna, która po prostu uzyskuje bezpośredni dostęp do elementów listy, zamiast je kopiować.

Pamiętaj tylko o sprawdzeniu zwrotu, iterator()ponieważ może być pusty lub nullzależy od implementacji.

jheddings
źródło
3
Należy zauważyć, że ta „sztuczka” działa tylko wtedy, gdy kolekcja rzeczywiście zawiera zawartość. Jeśli jest pusty, iterator może zwrócić błąd, w którym należy wcześniej sprawdzić rozmiar kolekcji.
spaceemotion
20
To powinna być poprawna odpowiedź. Nie rozumiem, dlaczego odpowiedź zawsze brzmi „użyj innej biblioteki!” .
Kuzeko
Co powiesz na drugi element kolekcji? Dlaczego first-> next () nie działa? Co powinienem zrobić? Dzięki!
pb772
nie jest wystarczająco bezpieczny, nie ma gwarancji, że kolekcja zawsze wskaże pierwszy element.
Następny programista
84

W java 8:

Optional<String> firstElement = collection.stream().findFirst();

W starszych wersjach java istnieje metoda getFirst w Guava Iterables :

Iterables.getFirst(iterable, defaultValue)
Vitalii Fedorenko
źródło
6
Rozwiązanie java 8 jest szczególnie przydatne, ponieważ obsługuje z wdziękiem przypadek, w którym kolekcja jest pusta.
SpaceTrucker
4
Niedobrze. Dodasz narzut stream (), aby uzyskać get (0) tylko dlatego, że jesteś leniwy, aby napisać 4 linie kodu. if (! CollectionUtils.isEmpty (productList)) {return Optional.of (productList.get (0)); } return Optional.empty ();
RS
Nie mam getFirstdostępnej metody. Istnieją geti getLastmetody
użytkownik1209216
4
@RS i co się stanie, jeśli nie możesz zadzwonić do productList.get (0), ponieważ jest to kolekcja ..? (Zgodnie z pytaniem OP)
Denham Coote
40

Nie ma czegoś takiego jak „pierwszy” element w Collection ponieważ jest to… no cóż, kolekcja.

Z metody Collection.iterator () Java doc :

Nie ma żadnych gwarancji dotyczących kolejności zwrotu elementów ...

Więc nie możesz.

Jeśli używasz innego interfejsu, takiego jak Lista , możesz wykonać następujące czynności:

String first = strs.get(0);

Ale bezpośrednio z kolekcji nie jest to możliwe.

OscarRyz
źródło
11
Nie sądzę, że get(int n)jest zdefiniowany dlaCollection
Nick Heiner
2
Masz rację, tęsknię za tym punktem. Zaktualizowałem odpowiedź. Nie możesz! (chyba że Kolekcja jest zaimplementowana przez jakąś klasę podkładową, która pozwala zapewnić gwarancję)
OscarRyz
get nie znajduje się w interfejsie Collection
Andy Gherna
21
Oscar, myślę, że przesadzasz. Pierwszy element kolekcji może być dowolny w niektórych przypadkach, np. HashSet, ale jest dobrze zdefiniowany: to .iterator (). Next (). Jest także stabilny w każdej implementacji kolekcji, jaką kiedykolwiek widziałem. (powiązane: zauważ, że chociaż Set nie gwarantuje zamówienia, każdy podtyp Set w JDK oprócz HashSet.)
Kevin Bourrillion
3
Być może, ale weź pod uwagę przypadek, gdy dodajesz nowy element do kolekcji, nie wiesz (przez interfejs), czy ten element jest pierwszy, ostatni, czy też zostanie wstawiony na środku. Aby uzyskać dokładne wyniki, powinieneś użyć innego interfejsu. Jednak prawdopodobnie to, czego potrzebuje Rosarch, to pierwszy element, bez względu na wszystko. Znajomość kolekcji podkładów może pomóc, ale uniemożliwi jej zmianę.
OscarRyz
4

Wygląda na to, że Twoja kolekcja chce być podobna do listy, więc sugeruję:

List<String> myList = new ArrayList<String>();
...
String first = myList.get(0);
Jim Ferrans
źródło
2

W Javie 8 masz do dyspozycji wielu operatorów, na przykład limit

     /**
 * Operator that limit the total number of items emitted through the pipeline
 * Shall print
 * [1]
 * @throws InterruptedException
 */
@Test
public void limitStream() throws InterruptedException {
    List<Integer> list = Arrays.asList(1, 2, 3, 1, 4, 2, 3)
                               .stream()
                               .limit(1)
                               .collect(toList());
    System.out.println(list);
}
Paweł
źródło
2
Odpowiedź @Vitalii Fedorenko stackoverflow.com/a/18165855/1562662 jest lepsza.
Chacko Mathew
2

Guava zapewnia onlyElement Collector, ale używaj go tylko wtedy, gdy oczekujesz, że kolekcja będzie miała dokładnie jeden element.

Collection<String> stringCollection = ...;
String string = collection.stream().collect(MoreCollectors.onlyElement())

Jeśli nie masz pewności, ile jest elementów, użyj findFirst.

Optional<String> optionalString = collection.stream().findFirst();
kambunkcyjny
źródło
1

Możesz wykonać casting. Na przykład, jeśli istnieje jedna metoda z tą definicją i wiesz, że ta metoda zwraca listę:

Collection<String> getStrings();

A po wywołaniu potrzebujesz pierwszego elementu, możesz to zrobić w następujący sposób:

List<String> listString = (List) getStrings();
String firstElement = (listString.isEmpty() ? null : listString.get(0));
Nacho Soriano
źródło
0

Jeśli wiesz, że kolekcja jest kolejką, możesz rzucić kolekcję do kolejki i łatwo ją zdobyć.

Istnieje kilka struktur, z których możesz skorzystać, aby uzyskać zamówienie, ale musisz do niego rzucić.

James Black
źródło
Zgadzam się, jeśli nie chcesz iterować, nie używaj kolekcji. Zamiast tego użyj innego, bardziej szczegółowego interfejsu.
Adeel Ansari
1
Zastanawiam się jednak ... powiedzmy, że rzeczywistymi danymi podstawowymi jest SortedSet, więc porządek ma sens, ale masz tylko widok kolekcji (powiedzmy, z niemądrego powodu); jeśli rzucisz Kolekcję na Listę, Kolejkę itp. i spróbujesz zdobyć / poll / etc, czy nastąpi katastrofa? Podobnie, jeśli podstawową strukturą jest Lista, itd., Itd.
Carl
@ Cal - nie próbowałem tego, ale jeśli rzucisz kolekcję na inny typ niż pierwotnie, powinieneś otrzymać błąd, ale ja nie próbowałem, więc mogłem się mylić.
James Black,
0

Zależy to całkowicie od zastosowanej implementacji, listy połączonych arraylist lub innych implementacji zestawu.

jeśli jest ustawiony, możesz bezpośrednio uzyskać pierwszy element, może to być pętla trick nad kolekcją, utwórz zmienną o wartości 1 i uzyskaj wartość, gdy wartość flagi wynosi 1 po tym przerwaniu tej pętli.

jeśli jest to implementacja listy, to łatwo jest zdefiniować numer indeksu.

Sindhoo Oad
źródło
0

Funkcjonalny sposób:

public static <T> Optional<T> findFirst(List<T> result) {
    return Optional.ofNullable(result)
            .map(List::stream)
            .flatMap(Stream::findFirst);
}

powyżej zachowaj fragment kodu z NullPointerException i IndexOutOfBoundsException

Farhad Baghirov
źródło
1
Twój wybór List<T>nie spełnia warunku, że powinien pracować dla Collection<String>, ale oczywiście, że mogą być ustalane za pomocą Collection<T>, z dodatkową zmianą: .map(Collection::stream).
Scratte
-2

Możesz to zrobić:

String strz[] = strs.toArray(String[strs.size()]);
String theFirstOne = strz[0];

Program javadoc dla kolekcji podaje następujące zastrzeżenie dotyczące uporządkowania elementów tablicy:

Jeśli ta kolekcja gwarantuje, w jakiej kolejności jej elementy są zwracane przez iterator, ta metoda musi zwrócić elementy w tej samej kolejności.

Andy Gherna
źródło
2
Tworzy to nową tablicę String, znacznie droższą niż tworzenie iteratora.
Jim Ferrans
Tak, pomyślałem o tym po tym, jak to opublikowałem. Bez względu na zastosowaną metodę, porządkowanie zależy od podstawowej implementacji Kolekcji. „Najpierw” staje się wówczas terminem względnym. Jednak metoda iteratora () jest prawdopodobnie lepsza w większości przypadków.
Andy Gherna
2
Przyjechałem tutaj, ponieważ to było moje rozwiązanie, które uważałem za brzydkie.
haansn08,