如何在 Java 中生成序列整数的列表或数组?

是否有一种简短而甜蜜的方法来生成一个 List<Integer>,或者可能是一个 Integer[]int[],其中包含从某个 start值到一个 end值的连续值?

也就是说,比下面的 1要短,但相当于 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;
}

番石榴的用途是罚款。

更新:

性能分析

由于这个问题已经得到了一些很好的回答,包括使用本地 Java8和第三方库,因此我认为应该测试所有解决方案的性能。

第一个测试使用以下方法简单地测试创建一个包含10个元素的 [1..10]列表:

  • ClassicArrayList : 上面问题中给出的代码(本质上与 adarshr 的答案相同)。
  • EclipseCollections : 下面 唐纳德的回答中给出的使用 Eclipse Collections8.0的代码。
  • GuavaRange : 下面 Daveb 的回答中给出的代码。从技术上讲,这并不创建一个 List<Integer>,而是一个 ContiguousSet<Integer>-但是因为它按顺序实现了 Iterable<Integer>,所以它基本上可以满足我的目的。
  • IntStreamRange : 下面的 弗拉基米尔的回答中给出的代码,它使用了 Java 8中引入的 IntStream.rangeClosed()-。
  • Stream Iterate : 下面的 卡特琳的回答中给出的代码也使用了 Java8中引入的 IntStream功能。

下面是每秒千次运算的结果(数字越大越好) ,适用于上面所有大小为10的列表:

List creation throughput

还有10000码的单子:

enter image description here

最后一张图表是正确的—— Eclipse 和 Guava 之外的解决方案都太慢了,甚至连一个像素条都没有!快速解决方案比其他方案快10,000到20,000个 时间

当然,这里发生的事情是,番石榴和 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)

... 和长度 = 10,000:

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

对许多元素的长时间迭代使事情变得更加平衡,但是即使在10,000个元素的测试中,Eclipse 和番石榴的速度仍然是原来的两倍以上。

所以如果你想使用 List<Integer>,Eclipse 集合看起来是最好的选择——当然,如果你以一种更原生的方式使用流(例如,忘记 .boxed()并在原始域中做一个简化) ,你可能会比所有这些变体更快结束。


也许除了错误处理,例如,如果 end < begin,或者如果大小超过一些实现或 JVM 限制(例如,数组大于 2^31-1

171151 次浏览

这是我使用核心 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;
}

嗯,这一个班轮可能符合条件(使用 番石榴山脉)

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

这不会创建一个 List<Integer>,但是 ContiguousSet提供了大致相同的功能,特别是实现了 Iterable<Integer>,它允许以与 List<Integer>相同的方式实现 foreach

在旧版本中(在番石榴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]

你可以用 番石榴山脉

你可以得到一个 SortedSet通过使用

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

这是我能找到的最短的了。

列表版本

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

这个也许对你有用。

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

Java8非常简单,甚至不再需要单独的方法:

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

在 Java 16或更高版本中:

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

下面的一行程序 Java8版本将生成[1,2,3... 10]。iterate的第一个参数是序列中的第一个 nr,而 limit的第一个参数是最后一个数。

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

您可以使用 Eclipse 集合中的 Interval类。

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

如果希望避免将 int 装箱为 Integers,但结果仍然希望使用列表结构,那么可以将 IntList与 Eclipse 集合中的 IntInterval一起使用。

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

IntList在接口上具有可用的方法 sum()min()minIfEmpty()max()maxIfEmpty()average()median()

明确更新: 2017年11月27日

IntervalList<Integer>,但它是惰性的和不可变的。它对于生成测试数据非常有用,特别是在处理大量集合时。如果你想你可以很容易地复制一个间隔到 ListSetBag如下:

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

IntInterval是扩展了 IntListImmutableIntList,它也有转换器方法。

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

IntervalIntInterval不具有相同的 equals合同。

Eclipse Collections9.0更新

现在可以从原始流创建原始集合。根据您的偏好,有 withAllofAll方法。如果你好奇,我解释为什么我们都有 给你。这些方法适用于可变和不可变的 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 集合的提交者

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