Ответ 1
С Java 8 он настолько прост, что больше не нужен отдельный метод:
List<Integer> range = IntStream.rangeClosed(start, end)
.boxed().collect(Collectors.toList());
Есть ли короткий и приятный способ генерировать 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]
, используя следующие методы:
List<Integer>
, а скорее ContiguousSet<Integer>
- но поскольку он реализует Iterable<Integer>
по порядку, он в основном работает для моих целей.IntStream.rangeClosed()
- который был представлен в Java 8.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
.
С Java 8 он настолько прост, что больше не нужен отдельный метод:
List<Integer> range = IntStream.rangeClosed(start, end)
.boxed().collect(Collectors.toList());
Ну, этот лайнер может подойти (использует диапазоны гуавы)
ContiguousSet<Integer> integerList = ContiguousSet.create(Range.closedOpen(0, 10), DiscreteDomain.integers());
System.out.println(integerList);
Это не создает List<Integer>
, но ContiguousSet
предлагает практически те же функциональные возможности, в частности реализацию Iterable<Integer>
, которая позволяет реализацию foreach
таким же образом, как List<Integer>
.
В старых версиях (где-то до Guava 14) вы могли использовать это:
ImmutableList<Integer> integerList = Ranges.closedOpen(0, 10).asSet(DiscreteDomains.integers()).asList();
System.out.println(integerList);
Оба продукта:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Следующая однострочная версия Java 8 будет генерировать [1, 2, 3... 10]. Первый arg iterate
- это первый nr в последовательности, а первый arg limit
- это последнее число.
List<Integer> numbers = Stream.iterate(1, n -> n + 1)
.limit(10)
.collect(Collectors.toList());
Это самый короткий, который я мог бы использовать с помощью Core Java.
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;
}
Вы можете использовать Interval
класс из Коллекции Eclipse.
List<Integer> range = Interval.oneTo(10);
range.forEach(System.out::print); // prints 12345678910
Класс Interval
ленив, поэтому не сохраняет все значения.
LazyIterable<Integer> range = Interval.oneTo(10);
System.out.println(range.makeString(",")); // prints 1,2,3,4,5,6,7,8,9,10
Ваш метод будет реализован следующим образом:
public List<Integer> makeSequence(int begin, int end) {
return Interval.fromTo(begin, end);
}
Если вы хотите избежать интеграции с целыми целями как целые, но по-прежнему будет выглядеть как структура списка, тогда вы можете использовать IntList
с IntInterval
из коллекций Eclipse.
public IntList makeSequence(int begin, int end) {
return IntInterval.fromTo(begin, end);
}
IntList
имеет методы sum()
, min()
, minIfEmpty()
, max()
, maxIfEmpty()
, average()
и median()
, доступные на интерфейсе.
Обновление для ясности: 11/27/2017
An Interval
является List<Integer>
, но он ленив и неизменен. Это чрезвычайно полезно для генерации тестовых данных, особенно если вы много разбираетесь в коллекциях. Если вы хотите, вы можете легко скопировать интервал в List
, Set
или Bag
следующим образом:
Interval integers = Interval.oneTo(10);
Set<Integer> set = integers.toSet();
List<Integer> list = integers.toList();
Bag<Integer> bag = integers.toBag();
An IntInterval
является ImmutableIntList
, который продолжается IntList
. Он также имеет методы конвертации.
IntInterval ints = IntInterval.oneTo(10);
IntSet set = ints.toSet();
IntList list = ints.toList();
IntBag bag = ints.toBag();
An Interval
и IntInterval
не имеют того же контракта equals
.
Обновление для Eclipse Collections 9.0
Теперь вы можете создавать примитивные коллекции из примитивных потоков. Существуют методы withAll
и ofAll
в зависимости от ваших предпочтений. Если вам интересно, я объясню, почему у нас есть здесь. Эти методы существуют для изменяемых и неизменных Int/Long/Double списков, наборов, сумм и стеков.
Assert.assertEquals(
IntInterval.oneTo(10),
IntLists.mutable.withAll(IntStream.rangeClosed(1, 10)));
Assert.assertEquals(
IntInterval.oneTo(10),
IntLists.immutable.withAll(IntStream.rangeClosed(1, 10)));
Примечание: я являюсь коммиттером для коллекций Eclipse
Вы можете использовать Диапазоны Guava
Вы можете получить SortedSet
с помощью
ImmutableSortedSet<Integer> set = Ranges.open(1, 5).asSet(DiscreteDomains.integers());
// set contains [2, 3, 4]
Это самое короткое, что я мог найти.
Список версий
public List<Integer> makeSequence(int begin, int end)
{
List<Integer> ret = new ArrayList<Integer>(++end - begin);
for (; begin < end; )
ret.add(begin++);
return ret;
}
Массивная версия
public int[] makeSequence(int begin, int end)
{
if(end < begin)
return null;
int[] ret = new int[++end - begin];
for (int i=0; begin < end; )
ret[i++] = begin++;
return ret;
}
Это может сработать для вас....
void List<Integer> makeSequence(int begin, int end) {
AtomicInteger ai=new AtomicInteger(begin);
List<Integer> ret = new ArrayList(end-begin+1);
while ( end-->begin) {
ret.add(ai.getAndIncrement());
}
return ret;
}