Java: ottieni il primo elemento da una raccolta


288

Se ho una collezione, ad esempio Collection<String> strs, come posso estrarre il primo oggetto? Potrei semplicemente chiamare un Iterator, prendere il suo primo next()e poi buttarlo Iteratorvia. C'è un modo meno dispendioso per farlo?


1
Ovviamente potrebbe esserci un modo migliore per accedere al primo elemento se conosci la classe contenitore di implementazione ...
Rooke


1
Sembra che tu abbia bisogno di Queue.peek ()
Johannes

Risposte:


131

Iterables.get (yourC, indexYouWant)

Perché in realtà, se utilizzi le raccolte, dovresti utilizzare le raccolte di Google.


7
Questo fa la stessa cosa, controlla prima se si tratta di un elenco e ottiene per indice se lo è. Ha anche del codice per tentare di fallire più velocemente su una vera collezione (cioè se l'indice è troppo grande cerca di capirlo senza iterare attraverso l'intera cosa e lanciare l'eccezione alla fine).
Yishai

1
Onestamente, dal punto di vista delle prestazioni potrebbe essere leggermente più lento di c.iterator (). Next () - ma il codice è molto più chiaro e semplice da modificare.
Carl il

2
Sono certamente d'accordo sul fatto che sia più pulito, ma l'OP era uno spreco, ma immagino che dal momento che la tua risposta è stata accettata sia ciò che si desiderava.
Yishai,

9
Per quelli che (ancora) arrivano qui: penso che la risposta di jheddings sia probabilmente la migliore risposta "fallo", anche se preferirei @ DonaldRaab (in fondo alla pagina) per i casi in cui sto già utilizzando la libreria GC. La mia risposta è davvero per il caso in cui si potrebbe voler scrivere in flessibilità per dopo (ad esempio, se si decide che il secondo elemento è il nuovo hotness).
Carl

4
A volte stai solo usando codice che usa le collezioni, quindi non c'è molto da fare.
erickrf

451

Sembra che questo sia il modo migliore per farlo:

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

Ottima domanda ... All'inizio sembra una svista per l' Collectioninterfaccia.

Nota che "first" non restituirà sempre la prima cosa che hai inserito nella raccolta e potrebbe avere senso solo per le raccolte ordinate. Forse è per questo che non c'è una get(item)chiamata, poiché l'ordine non è necessariamente preservato.

Anche se potrebbe sembrare un po 'dispendioso, potrebbe non essere così grave come pensi. In Iteratorrealtà contiene solo le informazioni di indicizzazione nella raccolta, di solito non una copia dell'intera raccolta. Invocare questo metodo crea un'istanza Iteratordell'oggetto, ma questo è davvero l'unico overhead (non come copiare tutti gli elementi).

Ad esempio, guardando il tipo restituito dal ArrayList<String>.iterator()metodo, vediamo che lo è ArrayList::Itr. Questa è una classe interna che accede direttamente agli elementi dell'elenco, invece di copiarli.

Assicurati solo di controllare il ritorno di iterator()poiché potrebbe essere vuoto o in nullbase all'implementazione.


3
È importante notare che questo "trucco" funziona solo quando la raccolta ha effettivamente dei contenuti. Se è vuoto, l'iteratore potrebbe restituire un errore, in cui è necessario verificare in anticipo la dimensione della raccolta.
spaceemotion

24
Questa dovrebbe essere la risposta corretta. Non capisco perché la risposta è sempre "usa un'altra libreria!" .
Kuzeko

E il secondo elemento della collezione? Perché first-> next () non funziona? Cosa dovrei fare? Grazie!
pb772

non abbastanza sicuro, non è garantito che la raccolta punti sempre al primo elemento.
Prossimo sviluppatore

89

In java 8:

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

Per le versioni precedenti di java, esiste un metodo getFirst in Guava Iterables :

Iterables.getFirst(iterable, defaultValue)

6
La soluzione java 8 è particolarmente utile, perché gestisce il caso in cui la raccolta è vuota con grazia.
SpaceTrucker

4
Non bene. Aggiungi overhead di stream () per ottenere un get (0) solo perché sei pigro a scrivere 4 righe di codice. if (! CollectionUtils.isEmpty (productList)) {return Optional.of (productList.get (0)); } return Optional.empty ();
RS

Non ho getFirstmetodo disponibile. Ci sono gete getLastmetodi
user1209216

5
@RS e cosa succede se non puoi chiamare productList.get (0), dato che è una raccolta ..? (Come da domanda OP)
Denham Coote

40

Non esiste un "primo" elemento in un Collectionperché è .. beh, semplicemente una collezione.

Dal metodo Collection.iterator () del documento Java :

Non ci sono garanzie sull'ordine in cui gli elementi vengono restituiti ...

Quindi non puoi.

Se utilizzi un'altra interfaccia come List , puoi eseguire le seguenti operazioni:

String first = strs.get(0);

Ma direttamente da una Collezione questo non è possibile.


12
Non credo get(int n)sia definito perCollection
Nick Heiner

2
Hai ragione, mi manca quel punto. Ho aggiornato la risposta. Non puoi! (a meno che la Collection non sia implementata da qualche classe underlaying che consente di fornire la garanzia)
OscarRyz

get non è nell'interfaccia della raccolta
Andy Gherna

21
Oscar, penso che tu stia esagerando con il caso. Il primo elemento di una Collection può essere arbitrario in alcuni casi come HashSet, ma è ben definito: è .iterator (). Next (). È anche stabile , in ogni implementazione di raccolta che abbia mai visto. (correlato: nota che mentre Set non garantisce l'ordine, ogni singolo sottotipo di Set nel JDK eccetto HashSet lo fa.)
Kevin Bourrillion

3
Potrebbe essere, ma considera il caso in cui quando aggiungi un nuovo elemento alla raccolta, non sai (dall'interfaccia) se quell'elemento è il primo, l'ultimo o sarebbe inserito nel mezzo. Per risultati precisi dovresti usare un'altra interfaccia. Eppure, probabilmente ciò di cui Rosarch ha bisogno è il primo elemento, qualunque cosa accada. Conoscere la raccolta del sottofondo può essere d'aiuto, ma ti impedisce di modificarla.
OscarRyz

4

Sembra che la tua raccolta desideri essere simile a un elenco, quindi suggerirei:

List<String> myList = new ArrayList<String>();
...
String first = myList.get(0);

2

In Java 8 hai molti operatori da usare, ad esempio 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);
}

2
La risposta di @Vitalii Fedorenko stackoverflow.com/a/18165855/1562662 è meglio.
Chacko Mathew

2

Guava fornisce un onlyElement Collector, ma usalo solo se ti aspetti che la raccolta abbia esattamente un elemento.

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

Se non sei sicuro di quanti elementi ci siano, usa findFirst.

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

1

Modo funzionale:

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

lo snippet di codice sopra riportato conserva da NullPointerException e IndexOutOfBoundsException


1
La vostra scelta di List<T>non soddisfa la condizione che dovrebbe lavorare per una Collection<String>, ma, naturalmente, che può essere fissato utilizzando Collection<T>, con il cambio supplementare: .map(Collection::stream).
Scratte il

0

Se sai che la raccolta è una coda, puoi eseguire il cast della raccolta in una coda e ottenerla facilmente.

Esistono diverse strutture che puoi utilizzare per ottenere l'ordine, ma dovrai eseguire il cast.


Sono d'accordo, se non vuoi iterare, non usare la raccolta. Usa invece un'altra interfaccia più specifica.
Adeel Ansari

1
Mi chiedo però ... diciamo che i dati sottostanti effettivi sono un SortedSet, quindi l'ordine ha un senso, ma hai solo una vista Collection (per una ragione non stupida, diciamo); se si trasmette la raccolta a un elenco, una coda, ecc. e si tenta di ottenere / sondare / ecc, si verifica un disastro? Allo stesso modo, se la struttura sottostante è una List, così via, così via.
Carl il

@Cal - Non l'ho provato, ma se lanci una raccolta in un tipo molto diverso da quello originariamente dovresti ricevere un errore, ma non l'ho provato, quindi potrei sbagliarmi.
James Black

0

Dipende totalmente dall'implementazione che hai usato, se arraylist linkedlist, o altre implementazioni di set.

se è impostato, puoi ottenere direttamente il primo elemento, il loro può essere un loop di trucco sulla raccolta, creare una variabile di valore 1 e ottenere valore quando il valore del flag è 1 dopo l'interruzione del ciclo.

se è l'implementazione della lista, allora è facile definendo il numero di indice.


0

Puoi fare un casting. Ad esempio, se esiste un metodo con questa definizione e sai che questo metodo restituisce un elenco:

Collection<String> getStrings();

E dopo averlo invocato, hai bisogno del primo elemento, puoi farlo in questo modo:

List<String> listString = (List) getStrings();
String firstElement = (listString.isEmpty() ? null : listString.get(0));

-2

Potresti farlo:

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

Il javadoc per Collection fornisce il seguente avvertimento rispetto all'ordine degli elementi dell'array:

Se questa raccolta fornisce garanzie sull'ordine in cui i suoi elementi vengono restituiti dal suo iteratore, questo metodo deve restituire gli elementi nello stesso ordine.


2
Questo crea un nuovo array di stringhe, molto più costoso della creazione di un iteratore.
Jim Ferrans

Sì, ci ho pensato dopo aver pubblicato questo. Indipendentemente dal metodo utilizzato, l'ordinamento dipende dall'implementazione sottostante della Collection. "Primo" diventa quindi un termine relativo. Tuttavia, il modo iterator () per farlo è probabilmente migliore nella maggior parte dei casi.
Andy Gherna

2
Sono venuto qui perché questa era la soluzione che avevo e ho trovato brutta.
haansn08
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.