Подтвердить что ты не робот

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

Есть ли короткий и приятный способ генерировать 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:

List creation throughput

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

enter image description here

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

List<Integer> Iteration (length=10)

... и длина = 10000:

List<Integer> Iteration (length=10,000)

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

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


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

4b9b3361

Ответ 1

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

List<Integer> range = IntStream.rangeClosed(start, end)
    .boxed().collect(Collectors.toList());

Ответ 2

Ну, этот лайнер может подойти (использует диапазоны гуавы)

    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]

Ответ 3

Следующая однострочная версия 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());

Ответ 4

Это самый короткий, который я мог бы использовать с помощью 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;  
}

Ответ 5

Вы можете использовать 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

Ответ 6

Вы можете использовать Диапазоны Guava

Вы можете получить SortedSet с помощью

ImmutableSortedSet<Integer> set = Ranges.open(1, 5).asSet(DiscreteDomains.integers());
// set contains [2, 3, 4]

Ответ 7

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

Список версий

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

Ответ 8

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

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