C'è un modo breve e dolce per generare un List<Integer>
, o forse un Integer[]
o int[]
, con valori sequenziali da un start
valore a un end
valore?
Cioè, qualcosa di più breve di, ma equivalente a 1, quanto segue:
void List<Integer> makeSequence(int begin, int end) {
List<Integer> ret = new ArrayList<>(end - begin + 1);
for (int i=begin; i<=end; i++) {
ret.add(i);
}
return ret;
}
L'uso della guava va bene.
Aggiornare:
Analisi di performance
Poiché questa domanda ha ricevuto diverse buone risposte, sia utilizzando Java 8 nativo che librerie di terze parti, ho pensato di testare le prestazioni di tutte le soluzioni.
Il primo test verifica semplicemente la creazione di un elenco di 10 elementi [1..10]
utilizzando i seguenti metodi:
- classicArrayList : il codice indicato sopra nella mia domanda (ed essenzialmente lo stesso della risposta di adarshr).
- eclipseCollections : il codice fornito nella risposta di Donald di seguito utilizzando Eclipse Collections 8.0.
- guavaRange : il codice fornito nella risposta di daveb di seguito. Tecnicamente, questo non crea un
List<Integer>
ma piuttosto unContiguousSet<Integer>
- ma poiché implementaIterable<Integer>
in ordine, funziona principalmente per i miei scopi. - intStreamRange : il codice fornito nella risposta di Vladimir di seguito, che utilizza
IntStream.rangeClosed()
- che è stato introdotto in Java 8. - streamIterate : il codice fornito nella risposta di Catalin di seguito che utilizza anche le
IntStream
funzionalità introdotte in Java 8.
Ecco i risultati in chilogrammi al secondo (numeri più alti sono migliori), per tutto quanto sopra con elenchi di dimensione 10:
... e ancora per elenchi di dimensioni 10.000:
Quest'ultimo grafico è corretto: le soluzioni diverse da Eclipse e Guava sono troppo lente per ottenere anche una singola barra di pixel! Le soluzioni rapide sono da 10.000 a 20.000 volte più veloci delle altre.
Quello che sta succedendo qui, ovviamente, è che le soluzioni guava ed eclipse in realtà non materializzano alcun tipo di elenco di 10.000 elementi: sono semplicemente wrapper di dimensioni fisse attorno all'inizio e agli endpoint. Ogni elemento viene creato secondo necessità durante l'iterazione. Poiché in realtà non iteriamo in questo test, il costo viene differito. Tutte le altre soluzioni materializzano effettivamente l'elenco completo in memoria e pagano un prezzo elevato in un benchmark di sola creazione.
Facciamo qualcosa di un po 'più realistico e ripetiamo anche tutti i numeri interi, sommandoli. Quindi, nel caso della IntStream.rangeClosed
variante, il benchmark assomiglia a:
@Benchmark
public int intStreamRange() {
List<Integer> ret = IntStream.rangeClosed(begin, end).boxed().collect(Collectors.toList());
int total = 0;
for (int i : ret) {
total += i;
}
return total;
}
Qui le immagini cambiano molto, anche se le soluzioni non materializzanti sono comunque le più veloci. Ecco la lunghezza = 10:
... e lunghezza = 10.000:
La lunga iterazione su molti elementi uniforma le cose molto, ma eclipse e guava rimangono più del doppio più veloci anche nel test di 10.000 elementi.
Quindi, se vuoi davvero una List<Integer>
, le raccolte di eclipse sembrano la scelta migliore, ma ovviamente se usi i flussi in un modo più nativo (ad esempio, dimenticando .boxed()
e riducendo il dominio primitivo) probabilmente finirai più veloce di tutti questi varianti.
1 Forse con l'eccezione della gestione degli errori, ad esempio, se end
< begin
, o se la dimensione supera alcuni limiti di implementazione o JVM (ad esempio, array più grandi di 2^31-1
.