如何在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和第三方库都可以很好地回答这个问题,所以我认为我应该测试所有解决方案的性能。

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

  • classicArrayList:我问题中上面给出的代码(与adarshr的回答基本相同)。
  • eclipseCollections:下面的Donald答案中使用Eclipse Collections 8.0 给出的代码。
  • guavaRange:下面的daveb答案中给出的代码。从技术上讲,这并不会创建List<Integer>而是ContiguousSet<Integer>-,但是由于它Iterable<Integer>是按顺序实现的,因此大多数情况下可以实现我的目的。
  • intStreamRange:下面弗拉基米尔的答案中给出的代码,它使用IntStream.rangeClosed()-在Java 8中引入的。
  • streamIterate:下面的Catalin答案中给出的代码,该代码也使用IntStreamJava 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:

List <整数>迭代(长度= 10)

...且长度= 10,000:

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

对许多元素的长时间迭代使事情变得更加平坦,但是即使在进行10,000个元素测试时,日食和番石榴的速度仍是其两倍以上。

因此,如果您真的想要List<Integer>,eclipse集合似乎是最佳选择-但是,如果您以更原生的方式使用流(例如,忘记.boxed()并进行原始域的缩减),则最终可能会比所有这些更快变体。


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


对于Apache Commons,stackoverflow.com
a / 5744861/560302

Answers:



28

好吧,这只班轮可能有资格(使用番石榴范围

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...,否则我不会在此处使用... ... ContiguousSetby产生的asSet是轻量级的(它只需要范围和域),但是asList()会创建一个列表,该列表实际将所有元素(当前)存储在内存中。
ColinD 2012年

1
同意 OP正在请求一个列表或数组,否则我会忽略它
daveb 2012年

1
我相信对于18.0,它Range存在但不存在,Ranges并且他们已经取消了该asSet方法。在我的旧版本中,asSet它已被弃用,并且似乎他们已将其删除。范围显然仅用于连续的集合,尽管我确实喜欢这种解决方案,但它们已经强制执行了。
demongolem

该API现在需要类似下面的代码:ContiguousSet.create(Range.closed(1,计数),DiscreteDomain.integers()

我在上面用标签guavaRange添加了此答案的性能结果。
BeeOnRope '16

11

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

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

我在上面用标签streamIterate添加了此答案的性能结果。
BeeOnRope '16

1
需要澄清的是,极限arg不是最后一个数字,而是列表中的整数数。
neilireson

7

您可以使用Eclipse Collections中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);
}

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

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

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

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

An Interval是一个List<Integer>,但它是惰性的并且是不变的。这对于生成测试数据非常有用,尤其是在您处理大量集合时。如果你愿意,你可以在一个时间间隔轻松复制到ListSetBag如下:

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

An IntIntervalImmutableIntList延伸的IntList。它还具有转换器方法。

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

An Interval和an IntInterval没有相同的equals合同。

Eclipse Collections 9.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 Collections的提交者


我在上面用标签eclipseCollections添加了此答案的性能结果。
BeeOnRope '16

整齐。我用其他原始版本更新了我的答案,该版本应该避免装箱。
唐纳德·拉布

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)零件移到for循环增量中是否会使这个“更短”。我猜按这种逻辑,如果我全部写在一行上,它会更短:)
BeeOnRope 2012年

@BeeOnRope是的,绝对不是最短的,但是肯定要短两行:)正如我说的,这是我们可以在Core Java中最短地缩短它的方法。
adarshr 2012年

我在上面用标签classicArrayList添加了此答案的性能结果。
BeeOnRope '16

3

您可以使用番石榴范围

您可以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非常繁重,在我的测试中速度要慢十倍左右。但这对于多线程是安全的。end <开始未验证
cl-r

1
在方法内部使用AtomicInteger没有意义。方法调用中的所有语句都由调用该方法的线程按顺序运行,因此您从AtomicInteger中什么也得不到,只有速度慢和令人讨厌的getAndIncrement()调用。
伊戈尔·罗德里格斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.