Reputation: 64997
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:
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:
List<Integer>
but rather a ContiguousSet<Integer>
- but since it implements Iterable<Integer>
in-order, it mostly works for my purposes.IntStream.rangeClosed()
- which was introduced in Java 8.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:
... and again for lists of size 10,000:
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:
... and 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
.
Upvotes: 196
Views: 215715
Reputation: 127971
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();
Upvotes: 297
Reputation: 5215
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);
Upvotes: 4
Reputation: 76241
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]
Upvotes: 31
Reputation: 6706
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
Upvotes: 12
Reputation: 781
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());
Upvotes: 22
Reputation: 62603
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;
}
Upvotes: 7
Reputation: 437
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;
}
Upvotes: 0
Reputation: 2940
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;
}
Upvotes: -2
Reputation: 5836
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]
Upvotes: 4