Как я могу создать список или массив последовательных целых чисел в Java?

129

Есть ли простой и приятный способ сгенерировать 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 ниже. Технически это создает не a, List<Integer>а скорее ContiguousSet<Integer>- но, поскольку он реализуется Iterable<Integer>по порядку, он в основном работает для моих целей.
  • intStreamRange : код, приведенный в ответе Владимира ниже, который использует IntStream.rangeClosed()- который был введен в Java 8.
  • streamIterate : код, приведенный в ответе Каталина ниже, который также использует IntStreamфункции, представленные в Java 8.

Вот результаты в килограммах операций в секунду (чем больше, тем лучше), для всего вышеперечисленного со списками размером 10:

Пропускная способность создания списка

... и снова для списков размером 10 000:

введите описание изображения здесь

Последняя диаграмма верна - решения, отличные от 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:

Список <Integer> Итерация (длина = 10)

... и длина = 10,000:

Список <Integer> Итерация (длина = 10 000)

Долгая итерация по многим элементам во многом уравновешивает ситуацию, но затмение и гуава остаются более чем в два раза быстрее даже при тесте на 10 000 элементов.

Так что, если вы действительно хотите List<Integer>, коллекции eclipse кажутся лучшим выбором, но, конечно, если вы используете потоки более естественным способом (например, забывая .boxed()и сокращая примитивный домен), вы, вероятно, закончите быстрее, чем все эти варианты.


1 Возможно, за исключением обработки ошибок, например, если end< begin, или если размер превышает некоторые ограничения реализации или JVM (например, массивы больше, чем 2^31-1.

BeeOnRope
источник
Для общего пользования apache, stackoverflow.com/a/5744861/560302
MoveFast

Ответы:

185

С Java 8 это настолько просто, что ему больше не нужен отдельный метод:

List<Integer> range = IntStream.rangeClosed(start, end)
    .boxed().collect(Collectors.toList());
Владимир Матвеев
источник
2
Я добавил результаты производительности для этого ответа выше с меткой intStreamRange .
BeeOnRope 07
1
Требуется API 24+
gcantoni
28

Что ж, этот лайнер может соответствовать требованиям (использует диапазоны Guava )

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]
daveb
источник
7
Я бы не стал использовать asList()его, если вам действительно не нужен List..., ContiguousSetсозданный с помощью asSetлегковесного (ему просто нужен диапазон и домен), но asList()создаст список, который фактически хранит все элементы в памяти (в настоящее время).
ColinD
1
Согласовано. OP запрашивал список или массив, иначе я бы его пропустил
daveb
1
Я считаю, что для 18.0 Rangeсуществует, но нет, Rangesи они покончили с этим asSetметодом. В моей более старой версии asSetон устарел и, похоже, они удалили его. Диапазоны, по-видимому, должны использоваться только для смежных коллекций, и они принудительно применяют это, хотя мне нравится это решение.
demongolem
Теперь API требует кода, подобного этому: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Бен
Я добавил результаты производительности для этого ответа выше с меткой guavaRange .
BeeOnRope 07
11

Следующая однострочная версия Java 8 сгенерирует [1, 2, 3 ... 10]. Первый аргумент iterate- это первый номер в последовательности, а первый аргумент limit- это последнее число.

List<Integer> numbers = Stream.iterate(1, n -> n + 1)
                              .limit(10)
                              .collect(Collectors.toList());
Каталин Чуреа
источник
Я добавил результаты производительности для этого ответа выше с помощью метки streamIterate .
BeeOnRope 07
1
В качестве пояснения: limit arg - это не последнее число, это количество целых чисел в списке.
neilireson
7

Вы можете использовать Intervalкласс из Eclipse Collections .

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);
}

Если вы не хотите помещать целые числа как целые числа, но все же хотите получить в результате структуру списка, вы можете использовать IntListwith IntIntervalfrom Eclipse Collections.

public IntList makeSequence(int begin, int end) {
    return IntInterval.fromTo(begin, end);
}

IntListесть методы sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()и median()доступны на интерфейсе.

Обновление для ясности: 27.11.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();

IntIntervalЭто ImmutableIntListкоторый простирается IntList. Также есть конвертерные методы.

IntInterval ints = IntInterval.oneTo(10);
IntSet set = ints.toSet();
IntList list = ints.toList();
IntBag bag = ints.toBag();

У An Intervalи an IntIntervalразные equalsконтракты.

Обновление для коллекций Eclipse 9.0

Теперь вы можете создавать примитивные коллекции из примитивных потоков. Существуют withAllи ofAllметоды в зависимости от ваших предпочтений. Если вам интересно, я объясню, почему у нас здесь оба . Эти методы существуют для изменяемых и неизменяемых Int / Long / Double Lists, Sets, Bags и Stacks.

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.

Дональд Рааб
источник
Я добавил результаты производительности для этого ответа выше с меткой eclipseCollections .
BeeOnRope 07
Ухоженная. Я обновил свой ответ дополнительной примитивной версией, которая должна избегать любого бокса.
Дональд Рааб
6

Это самое короткое, что я мог получить с помощью 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;  
}
adarshr
источник
3
Вы можете убрать еще пару символов, изменив этот цикл на for(int i = begin; i <= end; ret.add(i++));:)
vaughandroid
Я не совсем уверен, что перемещение ret.add(i)части в приращение цикла for сделает это «короче». Полагаю, по этой логике, если бы я написал все в одну строчку, было бы короче :)
BeeOnRope
@BeeOnRope Да, определенно не самый короткий, но точно на две строчки короче :) Как я уже сказал, это самое близкое к сокращению, которое мы можем сделать в Core Java.
adarshr
Я добавил результаты производительности для этого ответа выше с меткой classicArrayList .
BeeOnRope 07
3

Вы можете использовать диапазоны гуавы

Вы можете получить SortedSet, используя

ImmutableSortedSet<Integer> set = Ranges.open(1, 5).asSet(DiscreteDomains.integers());
// set contains [2, 3, 4]
jmruc
источник
0

Это самое короткое, что я смог найти.

Версия списка

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;
}
Код позади
источник
-2

Это может сработать для вас ....

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;  
}
Shehzad
источник
Использование AtomicInteger очень сложно для ресурсов, примерно в десять раз медленнее в моем тесте. Но это безопасно для многопоточности. конец <начало не проверено
cl-r
1
Использование AtomicInteger не имеет смысла внутри метода. Все предложения в вызове метода последовательно выполняются потоком, который вызвал метод, поэтому от AtomicInteger вы ничего не получаете, кроме замедления и раздражающих вызовов getAndIncrement ().
Игорь Родригес,