Як я можу генерувати список або масив послідовних цілих чисел на Java?


130

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

Ось результати в кіло-операціях за секунду (більші числа краще), для всіх перерахованих вище зі списками розміру 10:

Пропускна здатність створення списку

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

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

Остання остання діаграма є правильною - рішення, відмінні від Eclipse та Guava, занадто повільні, щоб отримати навіть одну піксельну смужку! Швидкі рішення в 10 000 - 20 000 разів швидші, ніж решта.

Тут, звичайно, відбувається те, що рішення гуави та затемнення насправді не реалізують будь-якого списку елементів 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)

... і довжина = 10000:

Список <Integer> Ітерація (довжина = 10 000)

Довга ітерація багатьох елементів збалансовує багато, але затемнення та гуава залишаються більш ніж удвічі швидшими навіть на тесті 10 000 елементів.

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


1 Можливо, за винятком поводження з помилками, наприклад, якщо end< begin, або якщо розмір перевищує деякі обмеження на реалізацію або JVM (наприклад, масиви більше, ніж 2^31-1.


Відповіді:


185

З Java 8 це так просто, тому він навіть не потребує окремого методу:

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

2
Вище додали результати для цієї відповіді з міткою intStreamRange .
BeeOnRope

Потрібен 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]

7
Я б не використовував asList()там, якщо вам справді не потрібен List... ContiguousSetвиробляється asSetневеликою вагою (йому просто потрібен діапазон і домен), але asList()створите список, який фактично зберігає всі елементи в пам'яті (в даний час).
ColinD

1
Домовились. ОП просила про перелік чи масив, хоча, інакше я б його покинув
Дейв

1
Я вважаю, що 18,0 Rangeіснує, але немає, Rangesі вони покінчили з asSetметодом. У моїй старій версії asSetця система застаріла, і видається, що її видалили. Діапазони, мабуть, використовуються лише для суміжних колекцій, і вони застосували його, хоча я люблю це рішення.
демонголем

API тепер вимагає код, подібний до цього: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Ben

Вище додали результати для цієї відповіді з міткою guavaRange .
BeeOnRope

11

Наступна однолінійна версія Java 8 генерує [1, 2, 3 ... 10]. Перший аргумент iterate- це перший nr у послідовності, а перший arg of limit- останнє число.

List<Integer> numbers = Stream.iterate(1, n -> n + 1)
                              .limit(10)
                              .collect(Collectors.toList());

Вище додали результати для цієї відповіді з міткою streamIterate .
BeeOnRope

1
Як уточнення, граничний аргумент - це не останнє число, це кількість цілих чисел у списку.
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);
}

Якщо ви хочете уникати бокс-ints як Integers, але все ж хочете в результаті структури списку, тоді ви можете використовувати IntListз IntIntervalEclipse Collections.

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

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

Оновлення для ясності: 27.11.2017

А 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();

IntervalІ IntIntervalне мають один і той же equalsконтракт.

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

Тепер ви можете створювати примітивні колекції з примітивних потоків. Існують withAllі ofAllметоди залежно від ваших уподобань. Якщо вам цікаво, я пояснюю, чому ми тут обоє . Ці методи існують для змінних та незмінних внутрішньо / довгих / подвійних списків, наборів, сумок та стеків.

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

Акуратний. Я оновив свою відповідь додатковою примітивною версією, яка повинна уникати будь-якого боксу.
Дональд Рааб

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

3
Ви можете поголити ще кілька символів, змінивши цю петлю на for(int i = begin; i <= end; ret.add(i++));:)
vaughandroid

Я не дуже впевнений, що переміщення ret.add(i)частини в приріст циклу робить це "коротшим". Я думаю, що за цією логікою, якби я написав все це на одному рядку, це було б коротше :)
BeeOnRope

@BeeOnRope Так, безумовно, не найкоротший, але коротший на два рядки точно :) Як я вже сказав, це найближче ми можемо підійти до його скорочення в Core Java.
adarshr

Вищі результати для цієї відповіді я додав вище за допомогою мітки classicArrayList .
BeeOnRope

3

Ви можете використовувати діапазони Guava

Ви можете отримати SortedSetза допомогою

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

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

Використання AtomicInteger дуже важке для ресурсів, приблизно в десять разів повільніше в моєму тесті. Але це безпечно для багатопотокових. кінець <початок не перевірено
cl-r

1
Використання AtomicInteger не має сенсу всередині методу. Усі пропозиції у виклику методу виконуються послідовно за потоком, який викликав метод, тому ви не отримуєте нічого від AtomicInteger, однак сповільнення та дратує виклики getAndIncrement ().
Ігор Родрігес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.