我目前正在承担一个项目,我正在使用Java微基准线束(JMH)框架测量Java中不同类型循环的速度。我得到了一些关于流的有趣结果,我无法解释,并且想知道是否更了解流和数组列表的人可以帮助我解释我的结果。
基本上,当遍历大小为100的数组列表时,stream.forEach方法比任何其他类型的循环都快得多:
我的结果图表显示在这里:https://i.imgur.com/ypXoWWq.png
我尝试过使用对象和字符串的数组列表,并且都产生类似的结果。随着列表的大小越来越大,流方法仍然更快,但其他列表之间的性能差距会变小。我不知道是什么导致了这些结果。
@Fork(5)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class StackOverflowQ {
List<Integer> intList;
int size = 100;
@Setup
public void setup() {
intList = new ArrayList<>(size);
for(int i = 0; i<size;i++){
intList.add(i);
}
}
/**
Work done to each item in the loop.
*/
public double doWork(int item) {
return item + 47;
}
@Benchmark
public void standardFor(Blackhole bh){
for (int i = 0; i<intList.size(); i++){
bh.consume(doWork(intList.get(i)));
}
}
@Benchmark
public void streamForEach(Blackhole bh){
intList.stream().forEach(i -> bh.consume(doWork(i)));
}
}
这个答案讨论了java.util.ArrayList
。其他Collection
实现可能(并且确实!)显示完全不同的结果。
简而言之:因为流使用Spliterator
而不是Iterator
基于< code>Iterator的迭代基于< code>hasNext和< code>next方法调用,其中第二个调用会使< code>Iterator实例发生变异。这带来了一些性能成本。< code > split operator 支持批量迭代。点击此处了解更多信息。增强的for循环(< code>for (T e : iterable) {...})似乎使用了< code >迭代器。
性能通常应该是次要问题;您应该使用最能描述您意图的结构。虽然由于向后兼容的原因,这将是困难的,但也许基于spliterator的forEach和增强的for循环(在< code>ArrayList上)之间的性能差异在将来会消失。
循环索引怎么样?(List#get(int)
)
它们显示出比分裂器更差的性能,部分原因是它们需要验证索引。其他原因可能包括方法调用,例如。在索引处获取数据,而Spliterator
直接访问数组。但这纯粹是猜测。
下面您可以看到一个微小的基准,它证实了所述的理论。请注意,最佳情况下,基准应该运行更长时间。
@State(Scope.Benchmark)
@Fork(value = 2)
@Warmup(iterations = 2, time = 3)
@Measurement(iterations = 2, time = 3)
public class A {
public List<Object> list;
@Setup
public void setup() {
list = new ArrayList<>();
for (int i = 0; i < 1000; i++) list.add(i);
}
@Benchmark
public void collectionForEach(Blackhole hole) {
list.forEach(hole::consume);
}
@Benchmark
public void iteratorFor(Blackhole hole) {
for (Iterator<Object> iterator = list.iterator(); iterator.hasNext(); ) {
hole.consume(iterator.next());
}
}
@Benchmark
public void enhancedFor(Blackhole hole) {
for (Object e : list) {
hole.consume(e);
}
}
@Benchmark
public void iteratorForEach(Blackhole hole) {
list.iterator().forEachRemaining(hole::consume);
}
@Benchmark
public void indexedFor(Blackhole hole) {
for (int i = 0, size = list.size(); i < size; i++) {
hole.consume(list.get(i));
}
}
@Benchmark
public void streamForEach(Blackhole hole) {
list.stream().forEach(hole::consume);
}
@Benchmark
public void spliteratorForEach(Blackhole hole) {
list.spliterator().forEachRemaining(hole::consume);
}
}
结果。“操作/秒”表示操作/秒,越高越好。
Benchmark Mode Cnt Score Error Units
A.collectionForEach thrpt 4 158047.064 ± 959.534 ops/s
A.iteratorFor thrpt 4 177338.462 ± 3245.129 ops/s
A.enhancedFor thrpt 4 177508.037 ± 1629.494 ops/s
A.iteratorForEach thrpt 4 184919.605 ± 1922.114 ops/s
A.indexedFor thrpt 4 193318.529 ± 2715.611 ops/s
A.streamForEach thrpt 4 280963.272 ± 2253.621 ops/s
A.spliteratorForEach thrpt 4 283264.539 ± 3055.967 ops/s
我遇到了一个非常奇怪的问题,java线程正忙着等待。 我有一个线程忙于等待其他线程的静态变量的状态。假设忙碌等待的线程正在等待另一个线程的静态int变量达到某个值 如果我使用上面的代码,线程将被卡在忙等待中,不会跳出while循环,即使确实达到5。 但是,如果我使用其他代码,那么线程确实会跳出忙等待循环。有时,一旦达到5,其他时候会晚一点。但它会发生。对于我的特定示例,我将其用作“无意义的工作”
我用Kotlin和Jongo来访问MongoDB。Jongo使用Jackson来序列化/反序列化对象,以便从MongoDB中保存和读取它们。我使用Jackson-Kotlin模块来帮助使用构造函数序列化Kotlin数据类。 下面是一个序列化良好的数据类的示例: 下面是一个未能反序列化的类似类的示例: Jongo抛出以下异常,因为Jackson反序列化失败: 如果我像这样完整地注释会话数据类,它确实
问题内容: 为什么 工作,但是 不是吗 问题答案: 为了理解这一点,让我们考虑一下编译器在两种可能性下每个步骤所做的事情。让我们开始: 编译器将‘4’转换为int。所以变成 然后编译器变成 ch是一个字符,编译器可以将54转换为字符,因为它可以证明转换没有损失。 现在让我们考虑第二个版本: ch在编译时没有已知值。因此,这成为 现在,编译器无法证明此(int)的结果在char范围内可存储。因此它
首先,我知道这个问题在许多其他线程中都有描述。但是我无法找到并回答这个问题,为什么这个错误并不总是被抛出? 让我描述一下我的意思。我写了一些示例代码来说明这一点: DSA 最后进行比较测试: 在运行时,我们会收到这个描述的问题 Java . lang . illegalargumentexception:比较法违反了它的通用契约! 根据托收文件。排序方法: (可选)如果实现检测到列表元素的自然排序
问题内容: 我正在尝试测试2个线程,一个线程具有较高的优先级,而另一个线程具有较低的优先级。 根据我的结果,有时低优先级线程速度更快,这怎么可能?我已经通过增加每个线程内的click变量来测试了不同优先级的线程。我也增加和减少了睡眠时间,但是什么也没有。 由于我是在没有后台运行繁重程序的情况下进行测试的,因此我决定在运行高清影片的情况下进行测试,但仍然没有真正的变化,线程的速度始终相同。 我的电脑
问题内容: 我希望下面的代码在上引发编译时错误,因为未声明为throw ,但编译成功(在Java 1.7.0_45中),并且如果编译时错误为,则会生成您期望的输出固定。 如果更改为,它也会编译。 不会按预期方式编译: 这样编译: 这不是: 这也可以编译: 一个更复杂的示例-被检查的异常由外部catch块捕获,而不是被声明为抛出。这样编译: 因此,当编译器可以确定捕获的异常始终合法地重新抛出时,似乎