Java 8 是否提供了一种重复值或函数的好方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18532488/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Does Java 8 provide a good way to repeat a value or function?
提问by Graeme Moss
In many other languages, eg. Haskell, it is easy to repeat a value or function multiple times, eg. to get a list of 8 copies of the value 1:
在许多其他语言中,例如。Haskell,很容易多次重复一个值或函数,例如。获取值 1 的 8 个副本的列表:
take 8 (repeat 1)
but I haven't found this yet in Java 8. Is there such a function in Java 8's JDK?
但我还没有在 Java 8 中找到这个。Java 8 的 JDK 中有这样的功能吗?
Or alternatively something equivalent to a range like
或者相当于一个范围的东西
[1..8]
It would seem an obvious replacement for a verbose statement in Java like
这似乎是 Java 中冗长语句的明显替代品,例如
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
to have something like
有类似的东西
Range.from(1, 8).forEach(i -> System.out.println(i))
though this particular example doesn't look much more concise actually... but hopefully it's more readable.
虽然这个特定的例子实际上看起来并没有更简洁……但希望它更具可读性。
采纳答案by assylias
For this specific example, you could do:
对于此特定示例,您可以执行以下操作:
IntStream.rangeClosed(1, 8)
.forEach(System.out::println);
If you need a step different from 1, you can use a mapping function, for example, for a step of 2:
如果您需要一个不同于 1 的步骤,您可以使用映射函数,例如,对于 2 的步骤:
IntStream.rangeClosed(1, 8)
.map(i -> 2 * i - 1)
.forEach(System.out::println);
Or build a custom iteration and limit the size of the iteration:
或者构建自定义迭代并限制迭代的大小:
IntStream.iterate(1, i -> i + 2)
.limit(8)
.forEach(System.out::println);
回答by clstrfsck
For completeness, and also because I couldn't help myself :)
为了完整性,也因为我无法帮助自己:)
Generating a limited sequence of constants is fairly close to what you would see in Haskell, only with Java level verboseness.
生成有限的常量序列与您在 Haskell 中看到的非常接近,只是具有 Java 级别的冗长性。
IntStream.generate(() -> 1)
.limit(8)
.forEach(System.out::println);
回答by Stuart Marks
Here's another technique I ran across the other day:
这是我前几天遇到的另一种技术:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
The Collections.nCopies
call creates a List
containing n
copies of whatever value you provide. In this case it's the boxed Integer
value 1. Of course it doesn't actually create a list with n
elements; it creates a "virtualized" list that contains only the value and the length, and any call to get
within range just returns the value. The nCopies
method has been around since the Collections Framework was introduced way back in JDK 1.2. Of course, the ability to create a stream from its result was added in Java SE 8.
该Collections.nCopies
调用会创建一个List
包含n
您提供的任何值的副本。在这种情况下,它是装箱Integer
值 1。当然,它实际上并没有创建一个包含n
元素的列表;它创建了一个仅包含值和长度的“虚拟化”列表,对get
范围内的任何调用都只返回该值。nCopies
自从 JDK 1.2 中引入集合框架以来,该方法就一直存在。当然,从结果创建流的能力是在 Java SE 8 中添加的。
Big deal, another way to do the same thing in about the same number of lines.
大不了,另一种在大约相同数量的行中做同样事情的方法。
However, this technique is faster than the IntStream.generate
and IntStream.iterate
approaches, and surprisingly, it's also faster than the IntStream.range
approach.
然而,这种技术比IntStream.generate
和IntStream.iterate
方法快,而且令人惊讶的是,它也比IntStream.range
方法快。
For iterate
and generate
the result is perhaps not too surprising. The streams framework (really, the Spliterators for these streams) is built on the assumption that the lambdas will potentially generate different values each time, and that they will generate an unbounded number of results. This makes parallel splitting particularly difficult. The iterate
method is also problematic for this case because each call requires the result of the previous one. So the streams using generate
and iterate
don't do very well for generating repeated constants.
对于iterate
和generate
结果也许不算稀奇。流框架(实际上,这些流的 Spliterators)建立在这样的假设之上,即 lambda 每次都可能生成不同的值,并且它们将生成无限数量的结果。这使得并行拆分特别困难。该iterate
方法在这种情况下也有问题,因为每次调用都需要前一次调用的结果。因此,使用generate
和的流iterate
在生成重复常量方面表现不佳。
The relatively poor performance of range
is surprising. This too is virtualized, so the elements don't actually all exist in memory, and the size is known up front. This should make for a fast and easily parallelizable spliterator. But it surprisingly didn't do very well. Perhaps the reason is that range
has to compute a value for each element of the range and then call a function on it. But this function just ignores its input and returns a constant, so I'm surprised this isn't inlined and killed.
相对较差的表现range
令人惊讶。这也是虚拟化的,所以元素实际上并不都存在于内存中,并且大小是预先知道的。这应该可以实现快速且易于并行化的拆分器。但出人意料的是,它做得并不好。也许原因是range
必须为范围的每个元素计算一个值,然后在其上调用一个函数。但是这个函数只是忽略它的输入并返回一个常量,所以我很惊讶它没有被内联和杀死。
The Collections.nCopies
technique has to do boxing/unboxing in order to handle the values, since there are no primitive specializations of List
. Since the value is the sameevery time, it's basically boxed once and that box is shared by all n
copies. I suspect boxing/unboxing is highly optimized, even intrinsified, and it can be inlined well.
该Collections.nCopies
技术必须进行装箱/拆箱才能处理值,因为 没有原始特化List
。由于每次的值都相同,所以它基本上被装箱一次,并且所有n
副本共享该框。我怀疑装箱/拆箱是高度优化的,甚至是内在化的,并且可以很好地内联。
Here's the code:
这是代码:
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();
}
And here are the JMH results: (2.8GHz Core2Duo)
这是 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
There is a fair amount of variance in the ncopies version, but overall it seems comfortably 20x faster than the range version. (I'd be quite willing to believe that I've done something wrong, though.)
ncopies 版本存在相当多的差异,但总体而言,它似乎比 range 版本快 20 倍。(不过,我很愿意相信我做错了什么。)
I'm surprised at how well the nCopies
technique works. Internally it doesn't do very much special, with the stream of the virtualized list simply being implemented using IntStream.range
! I had expected that it would be necessary to create a specialized spliterator to get this to go fast, but it already seems to be pretty good.
我对这项nCopies
技术的效果感到惊讶。在内部,它并没有什么特别之处,虚拟化列表的流只是使用IntStream.range
! 我原以为有必要创建一个专门的拆分器来使其快速运行,但它似乎已经很不错了。
回答by Hartmut P.
Once a repeat function is somewhere defined as
一旦重复函数在某处定义为
public static BiConsumer<Integer, Runnable> repeat = (n, f) -> {
for (int i = 1; i <= n; i++)
f.run();
};
You can use it now and then this way, e.g.:
您可以不时地以这种方式使用它,例如:
repeat.accept(8, () -> System.out.println("Yes"));
To get and equivalent to Haskell's
获得并等效于 Haskell 的
take 8 (repeat 1)
You could write
你可以写
StringBuilder s = new StringBuilder();
repeat.accept(8, () -> s.append("1"));