以下是以不同角度回答原帖的答案。首先,让我们来看一下如何快速/慢速迭代列表/集合。以下是在我的机器上执行的性能测试结果:
当: 字符串列表长度=100,线程数=1,循环次数=1000,单位=毫秒
原帖: 0.013
被接受的答案: 0.020
使用counter函数: 0.010
当: 字符串列表长度=1000_000,线程数=1,循环次数=100,单位=毫秒
原帖: 99.387
被接受的答案: 89.848
使用counter函数: 59.183
结论: 性能提升的百分比非常小,甚至更慢(如果字符串列表长度很小)。通常,通过更复杂的收集器来减少已加载到内存的列表/集合的迭代是错误的做法,你不会获得太多的性能提升。如果存在性能问题,我们应该寻找其他地方进行优化。
以下是我使用工具Profiler进行性能测试的代码:(我不打算在此讨论如何进行性能测试。如果您对测试结果有疑问,请使用您信任的任何工具重新进行测试)
@Test
public void test_46539786() {
final int strsLength = 1000_000;
final int threadNum = 1;
final int loops = 100;
final int rounds = 3;
final List<String> strs = IntStream.range(0, strsLength).mapToObj(i -> i % 2 == 0 ? i + " of " + i : i + " for " + i).toList();
Profiler.run(threadNum, loops, rounds, "OP", () -> {
List<Integer> wordsInStr = strs.stream().filter(t -> t.contains("of")).map(t -> t.split(" ").length).collect(Collectors.toList());
List<String> linePortionAfterFor = strs.stream().filter(t -> t.contains("for")).map(t -> t.substring(t.indexOf("for")))
.collect(Collectors.toList());
assertTrue(wordsInStr.size() == linePortionAfterFor.size());
}).printResult();
Profiler.run(threadNum, loops, rounds, "Accepted answer", () -> {
Splitter collect = strs.stream().collect(Collector.of(Splitter::new, Splitter::accept, Splitter::merge));
assertTrue(collect.counts.size() == collect.words.size());
}).printResult();
final Function<String, Integer> counter = s -> {
int count = 0;
for (int i = 0, len = s.length(); i < len; i++) {
if (s.charAt(i) == ' ') {
count++;
}
}
return count;
};
Profiler.run(threadNum, loops, rounds, "By the counter function", () -> {
List<Integer> wordsInStr = strs.stream().filter(t -> t.contains("of")).map(counter).collect(Collectors.toList());
List<String> linePortionAfterFor = strs.stream().filter(t -> t.contains("for")).map(t -> t.substring(t.indexOf("for")))
.collect(Collectors.toList());
assertTrue(wordsInStr.size() == linePortionAfterFor.size());
}).printResult();
}
contains("for")
紧随其后立即使用indexOf("for")
时,您并不需要在迭代两次时担心,尽管这些操作是相关的,并且可以融合而不损失易读性。而且,如果您真的关心性能,请思考当您只想计算空格数时,t.split(" ")
实际上执行了什么操作。当操作无关时迭代两次不是问题。 - Holgert.split(" ")
来获取实际单词数是错误的。但是,如果我没有元素列表,而是从网络连接或某个I/O通道获得的Stream
,那么这将显着改变用例,不是吗? - MC EmperorPair/Triple
:Function<? super T, Pair<R1, R2>> mapper = ...
,然后再考虑创建一些复杂的收集器。 - 123-xyzPair
不适用,因为在大多数情况下,for
和of
的出现次数是不同的,将结果放在一个Pair
中 a) 会导致一些Pair
没有左值或右值,b) 也不能反映出Pair
的语义。 - MC Emperor