Java 8 是否提供了一种重复值或函数的好方法?

新手上路,请多包涵

在许多其他语言中,例如。 Haskell,很容易多次重复一个值或函数,例如。获取值 1 的 8 个副本的列表:

 take 8 (repeat 1)

但是我还没有在Java 8中找到这个。Java 8的JDK中有这样的功能吗?

或者相当于一个范围的东西

[1..8]

这似乎是 Java 中冗长语句的明显替代品,例如

for (int i = 1; i <= 8; i++) {
    System.out.println(i);
}

有类似的东西

Range.from(1, 8).forEach(i -> System.out.println(i))

尽管这个特定示例实际上看起来并没有更简洁……但希望它更具可读性。

原文由 Graeme Moss 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 329
2 个回答

对于这个特定的例子,你可以这样做:

 IntStream.rangeClosed(1, 8)
         .forEach(System.out::println);

如果您需要不同于 1 的步骤,则可以使用映射函数,例如,对于步骤 2:

 IntStream.rangeClosed(1, 8)
         .map(i -> 2 * i - 1)
         .forEach(System.out::println);

或者构建自定义迭代并限制迭代的大小:

 IntStream.iterate(1, i -> i + 2)
         .limit(8)
         .forEach(System.out::println);

原文由 assylias 发布,翻译遵循 CC BY-SA 3.0 许可协议

这是我前几天遇到的另一种技术:

 Collections.nCopies(8, 1)
           .stream()
           .forEach(i -> System.out.println(i));

Collections.nCopies 调用创建一个 List 包含 n 您提供的任何值的副本。在这种情况下,它是盒装的 n Integer 值 1。它创建一个仅包含值和长度的“虚拟化”列表,并且在范围内对 get 的任何调用都只返回值。 nCopies 方法自从在 JDK 1.2 中引入集合框架以来就一直存在。当然,从其结果创建流的能力是在 Java SE 8 中添加的。

重要的是,另一种在大约相同数量的行中做同样事情的方法。

然而,这种技术比 IntStream.generateIntStream.iterate 方法更快,令人惊讶的是,它也比 IntStream.range 方法更快

对于 iterategenerate 结果可能并不太令人惊讶。流框架(实际上,这些流的 Spliterator)是建立在这样的假设之上的,即 lambda 表达式可能每次都生成不同的值,并且它们将生成无限数量的结果。这使得并行拆分特别困难。 iterate 方法对于这种情况也有问题,因为每次调用都需要前一次的结果。因此,使用 generateiterate 的流在生成重复常量方面效果不佳。

range 相对较差的性能令人惊讶。这也是虚拟化的,所以元素实际上并不都存在于内存中,并且大小是预先知道的。这应该有助于快速且易于并行化的拆分器。但令人惊讶的是,它并没有做得很好。也许原因是 range 必须计算范围内每个元素的值,然后对其调用函数。但是这个函数只是忽略它的输入并返回一个常量,所以我很惊讶它没有被内联和杀死。

Collections.nCopies 技术必须进行装箱/拆箱才能处理值,因为 List 没有原始特化。由于每次的值都 相同,所以它基本上装箱一次并且该箱子由所有 n 副本共享。我怀疑装箱/拆箱是高度优化的,甚至是内化的,并且可以很好地内联。

这是代码:

     public static final int LIMIT = 500_000_000;
    public static final long VALUE = 3L;

    public long range() {
        return
            LongStream.range(0, LIMIT)
                .parallel()
                .map(i -> VALUE)
                .map(i -> i % 73 % 13)
                .sum();
}

    public long ncopies() {
        return
            Collections.nCopies(LIMIT, VALUE)
                .parallelStream()
                .mapToLong(i -> i)
                .map(i -> i % 73 % 13)
                .sum();
}

这里是 JMH 结果:(2.8GHz Core2Duo)

 Benchmark                    Mode   Samples         Mean   Mean error    Units
c.s.q.SO18532488.ncopies    thrpt         5        7.547        2.904    ops/s
c.s.q.SO18532488.range      thrpt         5        0.317        0.064    ops/s

ncopies 版本有相当多的差异,但总体而言它似乎比范围版本快 20 倍。 (不过,我很愿意相信我做错了什么。)

我对 nCopies 技术的效果感到惊讶。在内部它并没有做太多特别的事情,虚拟列表的流只是使用 IntStream.range 实现的!我原以为有必要创建一个专门的分离器来让它快速运行,但它似乎已经很不错了。

原文由 Stuart Marks 发布,翻译遵循 CC BY-SA 3.0 许可协议

推荐问题