Есть ли короткий и приятный способ генерировать List<Integer>
или, возможно, Integer[]
или int[]
, с последовательными значениями от некоторого значения start
до значения end
?
То есть что-то короче, но эквивалентно 1 следующему:
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;
}
Хорошо использовать гуаву.
Обновление:
Анализ производительности
Поскольку на этот вопрос было получено несколько хороших ответов, как с использованием нативной Java 8, так и сторонних библиотек, я подумал, что протестирую производительность всех решений.
Первый тест просто тестирует создание списка из 10 элементов [1..10]
, используя следующие методы:
- classicArrayList: код, приведенный выше в моем вопросе (и, по сути, такой же, как и ответ adarshr).
- eclipseCollections: код, приведенный в ответе Дональда ниже с использованием Eclipse Collections 8.0.
- guavaRange: код, указанный в ответе daveb ниже. Технически это не создает
List<Integer>
, а скорееContiguousSet<Integer>
- но поскольку он реализуетIterable<Integer>
по порядку, он в основном работает для моих целей. - intStreamRange: код, приведенный в Владимирском ответе ниже, который использует
IntStream.rangeClosed()
- который был представлен в Java 8. - streamIterate: код, приведенный в ответе Catalin ниже, который также использует функциональность
IntStream
, представленную в Java 8.
Ниже приведены результаты в килограммах операций в секунду (чем выше число, тем лучше) для всех вышеперечисленных списков размером 10:
... и снова для списков размером 10000:
Эта последняя диаграмма верна - решения, кроме Eclipse и Guava, слишком медленные, чтобы даже получить однопиксельную полосу! Быстрые решения в 10–20 000 раз быстрее остальных.
Конечно, здесь происходит то, что решения guava и eclipse фактически не материализуют какой-либо список из 10 000 элементов - они просто являются фиксаторами фиксированного размера вокруг начальной и конечной точек. Каждый элемент создается по мере необходимости во время итерации. Поскольку в этом тесте мы не выполняем итерацию, стоимость откладывается. Все остальные решения на самом деле материализуют полный список в памяти и платят высокую цену за эталон только для создания.
Давайте сделаем что-то более реалистичное, а также переберем все целые числа, суммируя их. Таким образом, в случае варианта IntStream.rangeClosed
эталонный тест выглядит следующим образом:
@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;
}
Здесь картинки сильно меняются, хотя нематериальные решения по-прежнему самые быстрые. Здесь длина = 10:
... и длина = 10000:
Длинная итерация по многим элементам выравнивает многое, но затмение и гуава остаются более чем в два раза быстрее даже при тестировании 10000 элементов.
Так что если вы действительно хотите List<Integer>
, коллекции затмений кажутся лучшим выбором - но, конечно, если вы используете потоки более естественным образом (например, забываете .boxed()
и делаете сокращение в примитивной области), вы, вероятно, в конечном итоге быстрее, чем все эти варианты.
1 Возможно, за исключением обработки ошибок, например, если end
& lt; begin
, или если размер превышает некоторую реализацию или пределы JVM (например, массивы больше, чем 2^31-1
.