159

是否有一种简短而甜蜜的方法来生成一个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 还是第三方库,我想我会测试所有解决方案的性能。

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

  • classicArrayList:上面在我的问题中给出的代码(与 adarshr 的答案基本相同)。
  • eclipseCollections :使用 Eclipse Collections 8.0 在下面Donald 的回答中给出的代码。
  • guavaRange:下面daveb 的答案中给出的代码。从技术上讲,这不会创建一个List<Integer>,而是创建一个ContiguousSet<Integer>- 但由于它Iterable<Integer>是按顺序实现的,因此它主要适用于我的目的。
  • intStreamRange:下面Vladimir 的答案中给出的代码,它使用IntStream.rangeClosed()- 在 Java 8 中引入。
  • streamIterate:下面Catalin 的答案中给出的代码也使用IntStream了 Java 8 中引入的功能。

以下是每秒千次操作的结果(数字越大越好),对于上述所有大小为 10 的列表:

列表创建吞吐量

...对于大小为 10,000 的列表,再次:

在此处输入图像描述

最后一张图表是正确的——除了 Eclipse 和 Guava 之外的解决方案都太慢了,甚至无法获得单个像素条!快速解决方案比其他解决方案快 10,000 到 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> 迭代(长度=10)

... 并且长度 = 10,000:

List<Integer> 迭代(长度=10,000)

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

因此,如果你真的想要一个List<Integer>,eclipse 集合似乎是最好的选择——但当然,如果你以更原生的方式使用流(例如,忘记.boxed()并减少原始域),你最终可能会比所有这些更快变体。


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

4

9 回答 9

246

使用 Java 8,它非常简单,甚至不再需要单独的方法:

List<Integer> range = IntStream.rangeClosed(start, end)
    .boxed().collect(Collectors.toList());
于 2014-04-03T06:00:08.097 回答
30

好吧,这一个班轮可能符合条件(使用Guava Ranges

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]
于 2012-04-20T08:07:54.907 回答
16

以下单行 Java 8 版本将生成 [ 1, 2 ,3 ... 10 ]。第一个 arg ofiterate是序列中的第一个 nr,第一个 arg oflimit是最后一个数字。

List<Integer> numbers = Stream.iterate(1, n -> n + 1)
                              .limit(10)
                              .collect(Collectors.toList());
于 2016-03-20T14:04:12.273 回答
9

您可以使用Eclipse CollectionsInterval中的类。

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

如果您想避免将整数装箱为整数,但仍希望使用列表结构,那么您可以使用Eclipse Collections 中IntList的 with 。IntInterval

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

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

为清晰起见更新:2017 年 11 月 27 日

AnInterval是 a List<Integer>,但它是惰性且不可变的。它对于生成测试数据非常有用,尤其是在您处理大量集合时。如果您愿意,您可以轻松地将间隔复制到 a ListSet或者Bag如下所示:

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

AnIntInterval是 an ImmutableIntListwhich extends IntList。它也有转换器方法。

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

AnInterval和 anIntInterval没有相同的equals契约。

Eclipse Collections 9.0的更新

您现在可以从原始流创建原始集合。有withAllofAll方法取决于你的喜好。如果你好奇,我会解释为什么我们都在这里。这些方法适用于可变和不可变的 Int/Long/Double Lists、Sets、Bags 和 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)));

注意:我是 Eclipse Collections 的提交者

于 2016-04-01T07:09:19.877 回答
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;  
}
于 2012-04-20T08:07:38.650 回答
4

你可以使用番石榴范围

你可以SortedSet通过使用

ImmutableSortedSet<Integer> set = Ranges.open(1, 5).asSet(DiscreteDomains.integers());
// set contains [2, 3, 4]
于 2012-04-20T08:09:36.377 回答
1
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);
于 2021-03-15T07:28:06.323 回答
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;
}
于 2012-04-20T08:28:24.090 回答
-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;  
}
于 2012-04-20T08:29:26.977 回答