在许多其他语言中,例如.Haskell,很容易多次重复一个值或函数,例如.获取值为1的8个副本的列表:
take 8 (repeat 1)
Run Code Online (Sandbox Code Playgroud)
但我还没有在Java 8中找到它.在Java 8的JDK中是否有这样的功能?
或者相当于范围的东西
[1..8]
Run Code Online (Sandbox Code Playgroud)
它似乎是Java中冗长语句的明显替代品
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
Run Code Online (Sandbox Code Playgroud)
有类似的东西
Range.from(1, 8).forEach(i -> System.out.println(i))
Run Code Online (Sandbox Code Playgroud)
虽然这个特殊的例子实际上看起来并不简洁......但希望它更具可读性.
ass*_*ias 138
对于此特定示例,您可以执行以下操作:
IntStream.rangeClosed(1, 8)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
如果需要与1不同的步骤,则可以使用映射函数,例如,步骤2:
IntStream.rangeClosed(1, 8)
.map(i -> 2 * i - 1)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
或者构建自定义迭代并限制迭代的大小:
IntStream.iterate(1, i -> i + 2)
.limit(8)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
Stu*_*rks 58
这是我前几天遇到的另一种技术:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
Run Code Online (Sandbox Code Playgroud)
该Collections.nCopies调用会创建List包含n您提供的任何值的副本.在这种情况下,它是盒装Integer值1.当然,它实际上并不创建包含n元素的列表; 它创建一个"虚拟化"列表,只包含值和长度,对get范围内的任何调用只返回值.nCopies自从JDK 1.2中引入了Collections Framework以来,该方法就已存在.当然,在Java SE 8中添加了从其结果创建流的能力.
重要的是,在相同数量的线上做同样的事情的另一种方式.
然而,这种技术比接近IntStream.generate和IntStream.iterate接近更快,并且令人惊讶的是,它也比IntStream.range方法更快.
因为iterate,generate结果可能并不太令人惊讶.流框架(实际上,这些流的Spliterators)建立在lambda每次可能生成不同值的假设之上,并且它们将生成无限数量的结果.这使得并行分裂特别困难.该iterate方法对于这种情况也是有问题的,因为每次调用都需要前一次调用的结果.所以流使用generate和iterate不能很好地生成重复的常量.
相对较差的表现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();
}
Run Code Online (Sandbox Code Playgroud)
以下是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
Run Code Online (Sandbox Code Playgroud)
ncopies版本中存在相当大的差异,但整体而言它似乎比范围版本快20倍.(不过我会非常愿意相信我做错了.)
我对这项nCopies技术的运作情况感到惊讶.在内部,它没有太多特别之处,虚拟化列表的流只是使用IntStream.range!我原本以为有必要创建一个专门的分裂器来让它快速发展,但它似乎已经相当不错了.
msa*_*ord 32
为了完整,还因为我无法自救:)
生成有限的常量序列与您在Haskell中看到的非常接近,只有Java级别的冗长.
IntStream.generate(() -> 1)
.limit(8)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
Har*_* P. 10
一旦重复函数被定义为
public static BiConsumer<Integer, Runnable> repeat = (n, f) -> {
for (int i = 1; i <= n; i++)
f.run();
};
Run Code Online (Sandbox Code Playgroud)
你现在可以使用它,然后这样,例如:
repeat.accept(8, () -> System.out.println("Yes"));
Run Code Online (Sandbox Code Playgroud)
获得和等同于Haskell的
take 8 (repeat 1)
Run Code Online (Sandbox Code Playgroud)
你可以写
StringBuilder s = new StringBuilder();
repeat.accept(8, () -> s.append("1"));
Run Code Online (Sandbox Code Playgroud)