196

Is there a short and sweet way to generate a List<Integer>, or perhaps an Integer[] or int[], with sequential values from some start value to an end value?

That is, something shorter than, but equivalent to1 the following:

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

The use of guava is fine.

Update:

Performance Analysis

Since this question has received several good answers, both using native Java 8 and third party libraries, I thought I'd test the performance of all the solutions.

The first test simply tests creating a list of 10 elements [1..10] using the following methods:

  • classicArrayList: the code given above in my question (and essentially the same as adarshr's answer).
  • eclipseCollections: the code given in Donald's answer below using Eclipse Collections 8.0.
  • guavaRange: the code given in daveb's answer below. Technically, this doesn't create a List<Integer> but rather a ContiguousSet<Integer> - but since it implements Iterable<Integer> in-order, it mostly works for my purposes.
  • intStreamRange: the code given in Vladimir's answer below, which uses IntStream.rangeClosed() - which was introduced in Java 8.
  • streamIterate: the code given in Catalin's answer below which also uses IntStream functionality introduced in Java 8.

Here are the results in kilo-operations per second (higher numbers are better), for all the above with lists of size 10:

List creation throughput

... and again for lists of size 10,000:

enter image description here

That last chart is correct - the solutions other than Eclipse and Guava are too slow to even get a single pixel bar! The fast solutions are 10,000 to 20,000 times faster than the rest.

What's going on here, of course, is that the guava and eclipse solutions don't actually materialize any kind of 10,000 element list - they are simply fixed-size wrappers around the start and endpoints. Each element is created as needed during iteration. Since we don't actually iterate in this test, the cost is deferred. All of the other solutions actually materialize the full list in memory and pay a heavy price in a creation-only benchmark.

Let's do something a bit more realistic and also iterate over all the integers, summing them. So in the case of the IntStream.rangeClosed variant, the benchmark looks like:

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

Here, the pictures changes a lot, although the non-materializing solutions are still the fastest. Here's length=10:

List<Integer> Iteration (length=10)

... and length = 10,000:

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

The long iteration over many elements evens things up a lot, but eclipse and guava remain more than twice as fast even on the 10,000 element test.

So if you really want a List<Integer>, eclipse collections seems like the best choice - but of course if you use streams in a more native way (e.g., forgetting .boxed() and doing a reduction in the primitive domain) you'll probably end up faster than all these variants.


1 Perhaps with the exception of error handling, e.g., if end < begin, or if the size exceeds some implementation or JVM limits (e.g., arrays larger than 2^31-1.

1

9 Answers 9

298

With Java 8 it is so simple so it doesn't even need separate method anymore:

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

And in Java 16 or later:

List<Integer> range = IntStream.rangeClosed(start, end)
    .boxed().toList();
7
  • 2
    I added performance results for this answer above with the label intStreamRange.
    – BeeOnRope
    Commented Dec 7, 2016 at 21:02
  • 4
    Requires API 24+
    – gcantoni
    Commented Jun 20, 2020 at 10:03
  • How would I turn those numbers into strings? Commented Oct 25, 2020 at 22:38
  • 2
    @FelixJassler IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()), for example. Commented Oct 28, 2020 at 2:25
  • you could even do: List<Integer> range = IntStream.rangeClosed(start, end) .boxed().toList(); instead of the collector
    – moritz
    Commented May 14, 2022 at 9:59
31

Well, this one liner might qualify (uses Guava Ranges)

ContiguousSet<Integer> integerList = ContiguousSet.create(Range.closedOpen(0, 10), DiscreteDomain.integers());
System.out.println(integerList);

This doesn't create a List<Integer>, but ContiguousSet offers much the same functionality, in particular implementing Iterable<Integer> which allows foreach implementation in the same way as List<Integer>.

In older versions (somewhere before Guava 14) you could use this:

ImmutableList<Integer> integerList = Ranges.closedOpen(0, 10).asSet(DiscreteDomains.integers()).asList();
System.out.println(integerList);

Both produce:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5
  • 7
    I wouldn't use asList() there unless you really needed a List... the ContiguousSet produced by asSet is lightweight (it just needs the range and the domain), but asList() will create a list that actually stores all the elements in memory (currently).
    – ColinD
    Commented Apr 20, 2012 at 12:39
  • 1
    Agreed. The OP was asking for a List or array though, otherwise I would have left it out
    – daveb
    Commented Apr 20, 2012 at 13:51
  • 1
    I believe for 18.0, Range exists but not Ranges and they have done away with the asSet method. In my older version, asSet is deprecated and it appears that they have removed it. Ranges apparently are only to be used for contiguous collections and they have enforced it though I do love this solution.
    – demongolem
    Commented May 13, 2014 at 21:13
  • The API now requires code similar to this: ContiguousSet.create(Range.closed(1, count), DiscreteDomain.integers()
    – Ben
    Commented Aug 3, 2014 at 17:37
  • I added performance results for this answer above with the label guavaRange.
    – BeeOnRope
    Commented Dec 7, 2016 at 21:02
22

The following one-liner Java 8 version will generate [ 1, 2 ,3 ... 10 ]. The first arg of iterate is the first nr in the sequence, and the first arg of limit is the last number.

List<Integer> numbers = Stream.iterate(1, n -> n + 1)
                              .limit(10)
                              .collect(Collectors.toList());
3
  • I added performance results for this answer above with the label streamIterate.
    – BeeOnRope
    Commented Dec 7, 2016 at 21:03
  • 4
    As a point of clarification, the limit arg isn't the last number, it's the number of Integers in the list.
    – neilireson
    Commented Oct 28, 2018 at 8:23
  • 1
    When compared to the other approaches, this one allows you to create a sequence of integers with a step (e.g n -> n + 5), which is what brought me to this post in the first place.
    – Jason
    Commented Jul 18, 2023 at 20:29
12

You can use the Interval class from Eclipse Collections.

List<Integer> range = Interval.oneTo(10);
range.forEach(System.out::print);  // prints 12345678910

The Interval class is lazy, so doesn't store all of the values.

LazyIterable<Integer> range = Interval.oneTo(10);
System.out.println(range.makeString(",")); // prints 1,2,3,4,5,6,7,8,9,10

Your method would be able to be implemented as follows:

public List<Integer> makeSequence(int begin, int end) {
    return Interval.fromTo(begin, end);
}

If you would like to avoid boxing ints as Integers, but would still like a list structure as a result, then you can use IntList with IntInterval from Eclipse Collections.

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

IntList has the methods sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average() and median() available on the interface.

Update for clarity: 11/27/2017

An Interval is a List<Integer>, but it is lazy and immutable. It is extremely useful for generating test data, especially if you deal a lot with collections. If you want you can easily copy an interval to a List, Set or Bag as follows:

Interval integers = Interval.oneTo(10);
Set<Integer> set = integers.toSet();
List<Integer> list = integers.toList();
Bag<Integer> bag = integers.toBag();

An IntInterval is an ImmutableIntList which extends IntList. It also has converter methods.

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

An Interval and an IntInterval do not have the same equals contract.

Update for Eclipse Collections 9.0

You can now create primitive collections from primitive streams. There are withAll and ofAll methods depending on your preference. If you are curious, I explain why we have both here. These methods exist for mutable and immutable Int/Long/Double Lists, Sets, Bags and 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)));

Note: I am a committer for Eclipse Collections

2
  • 1
    I added performance results for this answer above with the label eclipseCollections.
    – BeeOnRope
    Commented Dec 7, 2016 at 21:02
  • Neat. I updated my answer with an additional primitive version that should avoid any boxing. Commented Dec 8, 2016 at 0:38
7

This is the shortest I could get using 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;  
}
4
  • 3
    You can shave off a couple more characters by changing that loop to for(int i = begin; i <= end; ret.add(i++)); :) Commented Apr 20, 2012 at 8:27
  • I'm not really sure that moving the ret.add(i) part into the for loop increment makes this "shorter". I guess by that logic if I wrote it all on one line it would be shorter :)
    – BeeOnRope
    Commented Apr 20, 2012 at 8:27
  • @BeeOnRope Yes, definitely not the shortest, but shorter by two lines for sure :) As I said, this is the closest we can come to shortening it in Core Java.
    – adarshr
    Commented Apr 20, 2012 at 9:21
  • I added performance results for this answer above with the label classicArrayList.
    – BeeOnRope
    Commented Dec 7, 2016 at 21:02
4

You could use Guava Ranges

You can get a SortedSet by using

ImmutableSortedSet<Integer> set = Ranges.open(1, 5).asSet(DiscreteDomains.integers());
// set contains [2, 3, 4]
4
int[] arr = IntStream.rangeClosed(2, 5).toArray();
System.out.println(Arrays.toString(arr));
// [2, 3, 4, 5]

Integer[] boxedArr = IntStream.rangeClosed(2, 5)
  .boxed().toArray(Integer[]::new);
System.out.println(Arrays.toString(boxedArr));

// Since Java 16
List<Integer> list1 = IntStream.rangeClosed(2, 5)
  .boxed().toList();
System.out.println(list1);

List<Integer> list2 = IntStream.rangeClosed(2, 5)
  .boxed().collect(Collectors.toList());
System.out.println(list2);

List<Integer> list3 = Arrays.asList(boxedArr);
System.out.println(list3);

List<Integer> list4 = new ArrayList<>();
IntStream.rangeClosed(2, 5).forEachOrdered(list4::add);
System.out.println(list4);
0

This is the shortest I could find.

List version

public List<Integer> makeSequence(int begin, int end)
{
    List<Integer> ret = new ArrayList<Integer>(++end - begin);

    for (; begin < end; )
        ret.add(begin++);

    return ret;
}

Array Version

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;
}
1
  • 3
    for (; begin < end; ) is better written while (begin < end)
    – Florian F
    Commented Jun 5, 2021 at 20:44
-2

This one might works for you....

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;  
}
2
  • Using AtomicInteger is very heavy for ressources, around ten time slower in my test. But it is secure for multithread. end<begin not verified
    – cl-r
    Commented Apr 20, 2012 at 12:12
  • 2
    The use of AtomicInteger doesn't make sense inside of a method. All the sentences in a method call are run sequentially by the thread that called the method, so you get nothing from the AtomicInteger but slowdown and annoying getAndIncrement() calls. Commented Aug 11, 2014 at 11:30

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.