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

在许多其他语言中,例如。 Haskell,很容易重复一个值或函数多次,例如。 得到8份价值清单1:

take 8 (repeat 1) 

但是我还没有在Java 8中find这个。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)) 

虽然这个特殊的例子看起来并不简单,但希望它更具可读性。

对于这个具体的例子,你可以这样做:

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

如果您需要与1不同的步骤,则可以使用映射function,例如,步骤为2:

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

或者构build一个自定义迭代并限制迭代的大小:

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

这是我另外一天运行的另一种技术:

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

Collections.nCopies调用创build一个List其中包含您提供的任何值的n副本。 在这种情况下,它是盒装的Integer值1.当然,它实际上并没有创build一个包含n元素的列表。 它会创build一个只包含值和长度的“虚拟化”列表,任何在范围内的调用get返回值。 自从Collections Framework在JDK 1.2中引入之后, nCopies方法就已经出现了。 当然,在Java SE 8中添加了从结果创buildstream的function。

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

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

对于iterategenerate结果可能不是太奇怪。 stream框架(实际上,这些stream的Spliterator)build立在lambda将会每次可能产生不同值的假设上,并且它们将产生无限数量的结果。 这使得平行分裂特别困难。 iterate方法对于这种情况也是有问题的,因为每个调用都需要前一个调用的结果。 所以使用generateiterate的stream不能很好地生成重复的常量。

range相对较差的performance令人惊讶。 这也是虚拟化的,所以元素实际上并不全都存在于内存中,并且尺寸是事先知道的。 这应该是一个快速和容易并行的分割器。 但是,令人惊讶的是,这并没有做得很好。 也许原因是range必须为range的每个元素计算一个值,然后调用一个函数。 但是这个函数只是忽略了它的input并返回一个常量,所以我很惊讶这不是内联和死亡。

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 csqSO18532488.ncopies thrpt 5 7.547 2.904 ops/s csqSO18532488.range thrpt 5 0.317 0.064 ops/s 

在ncopies版本中有相当大的差异,但是总体来说,它似乎比范围版本快20倍。 (不过,我很乐意相信我做错了什么)。

我很惊讶nCopies技术的工作。 在内部,它并没有什么特别之处,虚拟化列表的stream只是使用IntStream.range来实现的! 我原本以为有必要创build一个专门的分裂者来让这个快速发展,但它已经很不错了。

为了完整性,也因为我忍不住:)

生成一个有限的常量序列非常接近你在Haskell中看到的,只有Java级别的冗长。

 IntStream.generate(() -> 1) .limit(8) .forEach(System.out::println); 

一旦重复函数定义为某处

 public static BiConsumer<Integer, Runnable> repeat = (n, f) -> { for (int i = 1; i <= n; i++) f.run(); }; 

你现在可以用这个方法,例如:

 repeat.accept(8, () -> System.out.println("Yes")); 

获得和Haskell相当的

 take 8 (repeat 1) 

你可以写

 StringBuilder s = new StringBuilder(); repeat.accept(8, () -> s.append("1"));