这不是重复我的问题,我查了一下,更多的是关于内部匿名类。
我对Lambda表达式很好奇,并测试了以下内容:
最初的结果并不令人惊讶,因为我不知道自己会想出什么:
final int NUMBER_OF_LIST_INDEXES = 10_000;
List<String> myList = new ArrayList<>();
String[] myWords = "Testing Lamba expressions with this String array".split(" ");
for (int i = 0 ; i < NUMBER_OF_LIST_INDEXES ; i++){
myList.add(myWords[i%6]);
}
long time = System.currentTimeMillis();
// BOTH TESTS WERE RUN SEPARATELY OF COURSE
// PUT THE UNUSED ONE IN COMMENTS WHEN THE OTHER WAS WORKING
// 250 milliseconds for the Lambda Expression
myList.removeIf(x -> x.contains("s"));
// 16 milliseconds for the traditional Loop
for (int i = NUMBER_OF_LIST_INDEXES - 1 ; i >= 0 ; i--){
if (myList.get(i).contains("s")) myList.remove(i);
}
System.out.println(System.currentTimeMillis() - time + " milliseconds");
但后来,我决定将常量列表索引的数量
改为一百万,结果如下:
final int NUMBER_OF_LIST_INDEXES = 1_000_000;
List<String> myList = new ArrayList<>();
String[] myWords = "Testing Lamba expressions with this String array".split(" ");
for (int i = 0 ; i < NUMBER_OF_LIST_INDEXES ; i++){
myList.add(myWords[i%6]);
}
long time = System.currentTimeMillis();
// BOTH TESTS WERE RUN SEPARATELY OF COURSE
// PUT THE UNUSED ONE IN COMMENTS WHEN THE OTHER WAS WORKING
// 390 milliseconds for the Lambda Expression
myList.removeIf(x -> x.contains("s"));
// 32854 milliseconds for the traditional Loop
for (int i = NUMBER_OF_LIST_INDEXES - 1 ; i >= 0 ; i--){
if (myList.get(i).contains("s")) myList.remove(i);
}
System.out.println(System.currentTimeMillis() - time + " milliseconds");
为了让阅读更简单,以下是结果:
| | 10.000 | 1.000.000 |
| LAMBDA | 250ms | 390ms | 156% evolution
|FORLOOP | 16ms | 32854ms | 205000+% evolution
我有以下问题:
>
这背后的魔力是什么?当要使用的索引是*100时,我们如何对数组和lambda产生如此大的差异。
就性能而言,我们如何知道何时使用Lambdas,何时坚持传统的数据处理方式?
这是List
方法的特定行为吗?其他Lambda表达式是否也会产生像这样的随机性能?
我认为您看到的性能差异可能更多地是由于removeIf
在内部使用迭代器,而不是在for循环中使用get和remove。本PAQ中的答案提供了一些关于迭代器好处的好信息。
河口。io的答案是正确的,您可以在这里看到removeIf的代码。如果它进行两次传递,以避免反复移动其余元素。
我写了一个JMH基准来衡量这一点。其中有4种方法:
ArrayList
上的removeIf
LinkedList
上的removeIf
迭代器的迭代器。在ArrayList
上删除()
带有迭代器的迭代器。在链接列表上删除()
基准测试的目的是显示
RemveIF
和迭代器应该提供相同的性能,但是ArrayList
的情况并非如此。
默认情况下,
RemveIF
在内部使用迭代器来删除元素,因此我们应该期望RemveIF
和迭代器
具有相同的性能。
现在考虑一个<代码> ARAYLISTAB/COD>,它使用数组内部保存元素。每次我们调用
remove
,索引后的剩余元素必须移位一;所以每次都要复制很多元素。当使用迭代器遍历ArrayList
时,我们需要删除一个元素,这种复制需要一次又一次地进行,这使得复制非常缓慢。对于LinkedList
,情况并非如此:删除一个元素时,唯一的更改是指向下一个元素的指针。
那么为什么
ArrayList
上的removeIf
与LinkedList上的
一样快呢?因为它实际上是被重写的,并且不使用迭代器:代码实际上在第一个过程中标记要删除的元素,然后在第二个过程中删除它们(移动剩余的元素)。在这种情况下,优化是可能的:我们只在知道所有需要删除的元素时才进行一次,而不是每次需要删除的元素时都移动剩余的元素。
结论:
当需要删除与谓词匹配的所有元素时,应使用removeIf
基准测试结果:
Benchmark Mode Cnt Score Error Units
RemoveTest.removeIfArrayList avgt 30 4,478 ± 0,194 ms/op
RemoveTest.removeIfLinkedList avgt 30 3,634 ± 0,184 ms/op
RemoveTest.removeIteratorArrayList avgt 30 27197,046 ± 536,584 ms/op
RemoveTest.removeIteratorLinkedList avgt 30 3,601 ± 0,195 ms/op
基准:
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
@State(Scope.Benchmark)
public class RemoveTest {
private static final int NUMBER_OF_LIST_INDEXES = 1_000_000;
private static final String[] words = "Testing Lamba expressions with this String array".split(" ");
private ArrayList<String> arrayList;
private LinkedList<String> linkedList;
@Setup(Level.Iteration)
public void setUp() {
arrayList = new ArrayList<>();
linkedList = new LinkedList<>();
for (int i = 0 ; i < NUMBER_OF_LIST_INDEXES ; i++){
arrayList.add(words[i%6]);
linkedList.add(words[i%6]);
}
}
@Benchmark
public void removeIfArrayList() {
arrayList.removeIf(x -> x.contains("s"));
}
@Benchmark
public void removeIfLinkedList() {
linkedList.removeIf(x -> x.contains("s"));
}
@Benchmark
public void removeIteratorArrayList() {
for (ListIterator<String> it = arrayList.listIterator(arrayList.size()); it.hasPrevious();){
if (it.previous().contains("s")) it.remove();
}
}
@Benchmark
public void removeIteratorLinkedList() {
for (ListIterator<String> it = linkedList.listIterator(linkedList.size()); it.hasPrevious();){
if (it.previous().contains("s")) it.remove();
}
}
public static void main(String[] args) throws Exception {
Main.main(args);
}
}
因为remove(index)
非常昂贵:)它需要复制和移动其余的元素,这在您的情况下会执行多次。
而removeIf(过滤器)
不需要这样做。它可以扫描一次并标记所有要删除的元素;最后一个阶段只会将幸存者复制到名单的最前面一次。
问题内容: 在Java 8之前,可以通过使用匿名内部类来实现lambda功能。例如: 在性能方面,仍然使用这种方法和使用新的Java 8 lambda之间有区别吗? 问题答案: 甲骨文发布了一项研究,比较了Lambda和匿名类之间的性能 请参见Sergey Kuksenko撰写的JDK 8:Lambda性能研究 ,该幻灯片长74张。 简介:预热缓慢,但是当JIT内联时,最坏的情况与匿名类一样快,但
我有一个由4个节点组成的Cassandra(2.2.1)集群,由Java客户端应用程序使用。复制因子为3,读写的一致性级别为LOCAL_QUORUM。每个节点大约有5 GB的数据。请求量约为每秒2-4k。几乎没有删除操作,因此创建了少量的墓碑。 一段时间前,我注意到读写性能很差,而且随着时间的推移,性能越来越差——集群变得非常慢。读取(通常)和写入超时已变得非常频繁。硬件不应该引起问题,部署集群的
问题内容: 我正在计算稀疏自动编码器的算法。我已经使用和在python中实现了它。代码几乎相同,但是性能却大不相同。matlab完成任务所需的时间为0.252454秒,而numpy为0.973672151566,几乎是原来的四倍。在最小化问题中,我将在以后多次调用此代码,因此这种差异会导致实现之间的延迟几分钟。这是正常行为吗?如何提高numpy的性能? numpy实现: Sparse.rho是调整
是的,这是一个老话题,但我还是有些困惑。 在爪哇,人们说: LinkedList的插入速度比ArrayList快。这里插入是什么意思?如果这意味着向后移动一些元素,然后将元素放在中间的空点,那么ArrayList应该比LinkedList慢。如果插入只意味着添加(对象)操作,这怎么会慢呢?
我有两个收藏品 员额:
上次,我发现了Java8及以上版本函数式编程的难点,并在Collectors类中发现了一个静态方法。 我们有一个类员工像: 假设我们有一个类的POJO列表,并且我们希望接收一个所有员工姓名的列表。我们有两种方法,比如: 我知道第一种方法在上使用终端操作,而第二种方法在上使用中间操作,但我想知道第一种方法的性能是否比第二种方法差,反之亦然。如果您能解释第一种情况的潜在性能下降,当我们的数据源(emp