Czy istnieje krótki i przyjemny sposób generowania a List<Integer>
, a może Integer[]
lub lub int[]
z sekwencyjnymi wartościami od jakiejś start
wartości do end
wartości?
To znaczy coś krótszego niż, ale równoważne 1 z następującego:
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;
}
Używanie guawy jest w porządku.
Aktualizacja:
Analiza wydajności
Ponieważ to pytanie otrzymało kilka dobrych odpowiedzi, zarówno przy użyciu natywnych bibliotek Java 8, jak i bibliotek innych firm, pomyślałem, że przetestuję wydajność wszystkich rozwiązań.
Pierwszy test polega po prostu na przetestowaniu tworzenia listy 10 elementów [1..10]
za pomocą następujących metod:
- classicArrayList : kod podany powyżej w moim pytaniu (i zasadniczo taki sam jak odpowiedź adarshr).
- eclipseCollections : kod podany w odpowiedzi Donalda poniżej przy użyciu Eclipse Collections 8.0.
- guavaRange : kod podany w odpowiedzi daveb poniżej. Technicznie rzecz biorąc, nie tworzy to,
List<Integer>
ale raczejContiguousSet<Integer>
- ale ponieważ wdraża sięIterable<Integer>
w kolejności, działa głównie do moich celów. - intStreamRange : kod podany w odpowiedzi Vladimira poniżej, który używa
IntStream.rangeClosed()
- który został wprowadzony w Javie 8. - streamIterate : kod podany w poniższej odpowiedzi Catalina, który również wykorzystuje
IntStream
funkcjonalność wprowadzoną w Javie 8.
Oto wyniki w kilo operacjach na sekundę (wyższe liczby są lepsze), dla wszystkich powyższych z listami o rozmiarze 10:
... i jeszcze raz dla list o rozmiarze 10000:
Ten ostatni wykres jest poprawny - rozwiązania inne niż Eclipse i Guava są zbyt wolne, aby uzyskać nawet pojedynczy pasek pikseli! Szybkie rozwiązania są od 10 000 do 20 000 razy szybsze niż pozostałe.
Oczywiście chodzi tutaj o to, że rozwiązania z guawy i zaćmienia w rzeczywistości nie materializują żadnej listy 10 000 elementów - są po prostu opakowaniami o stałym rozmiarze wokół punktów początkowych i końcowych. Każdy element jest tworzony w razie potrzeby podczas iteracji. Ponieważ w rzeczywistości nie wykonujemy iteracji w tym teście, koszt jest odroczony. Wszystkie inne rozwiązania faktycznie materializują pełną listę w pamięci i płacą wysoką cenę w benchmarku tylko do tworzenia.
Zróbmy coś bardziej realistycznego, a także powtórzmy wszystkie liczby całkowite, sumując je. Czyli w przypadku IntStream.rangeClosed
wariantu benchmark wygląda następująco:
@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;
}
Tutaj obraz bardzo się zmienia, choć wciąż najszybsze są niematerialne rozwiązania. Oto długość = 10:
... i długość = 10000:
Długa iteracja wielu elementów bardzo wyrównuje sytuację, ale zaćmienie i guawa pozostają ponad dwukrotnie szybsze nawet w teście 10000 elementów.
Więc jeśli naprawdę chcesz List<Integer>
, kolekcje zaćmienia wydają się najlepszym wyborem - ale oczywiście, jeśli używasz strumieni w bardziej natywny sposób (np. Zapominając .boxed()
i redukując prymitywną domenę), prawdopodobnie skończysz szybciej niż wszystkie te warianty.
1 Być może z wyjątkiem obsługi błędów, np. If end
< begin
, lub jeśli rozmiar przekracza pewne limity implementacji lub JVM (np. Tablice większe niż 2^31-1
.