例如,有限量的元素的最小代码是最小的:
public int min(String s) {
return s.chars().map(this::mapToFactor).min().getAsInt();
}
private int mapToFactor(int ch) {
switch(ch) {
case 'A': return 1;
case 'C': return 2;
case 'G': return 3;
case 'T': return 4;
default: return Integer.MAX_VALUE;
}
}
Totaly只存在5个数字:1,2,3,4,Integer.MAX_VALUE。当我们面对1然后可以跳过将来的迭代并返回结果。
public int min(String s) {
int min = Integer.MAX_VALUE;
for (Character ch : s.toCharArray()) {
int current = mapToFactor(ch);
if(current == 1) {
//How I can implement this in Java 8 stream style?
return 1;
}
if (current < min) {
min = current;
}
return min;
}
}
因此,如果我们的String会很大,那么我们可以通过使用Java 8流而不是Java 7样式来显着降低性能,如果找到1则跳过iterrations。
请您解释一下如何用java 8流式编写上面的Java 7代码?
答案 0 :(得分:3)
下面的解决方案使用Java 9中引入的 takeWhile 方法。尽管如此,代码仍然是Java 8流式。
public int min(String s) {
IntSummaryStatistics statistics = s.chars().map(this::mapToFactor)
.takeWhile(i -> i != 1).summaryStatistics();
int index = (int)statistics.getCount();
return (index < s.length() && s.charAt(index) == 'A') ? 1 : statistics.getMin();
}
答案 1 :(得分:2)
这是典型的过早优化案例。如果你关心性能,短路迭代,是最后一件事,你应该担心。
让我们看看你的Java 7变体:
for (Character ch : s.toCharArray()) {
在开始迭代之前,您正在调用String.toCharArray()
,它会在新分配的String
对象中创建char[]
内容的副本。当然,要创建该副本,实现必须遍历整个String
。在您自己的迭代开始之前。
然后,您将每个char
值装入Character
对象。由于无法识别原因,因为mapToFactor
方法需要int
值,因此Character
对象必须在那里取消装箱。
由于这些原因,在大多数环境中,s.chars().map(this::mapToFactor).min().getAsInt()
可能比大型字符串的Java 7变体快得多。特别是,当我们认为拥有A
,即达到1
的最小值并且能够提前退出时,并非总是如此。
通常,您应该测量实际执行时间,而不是猜测特定方法的假定缺陷。如果遇到实际性能问题,只能开始尝试优化。由于您对创建String
的完整副本的原始代码感到满意,因此您应该对Stream变体感到满意,而不需要那些不必要的副本。在内联和分析代码之后,HotSpot优化器甚至可能会为Stream的内部循环添加提前终止条件。
答案 2 :(得分:1)
您可以运行Stream
管道来搜索第一次出现的1
。问题是如果找不到1
,您必须运行另一个Stream
管道以找到最小值。
我能想到的另一种方法是运行Stream
管道搜索第一个1
,同时使用peek
保持当前最小值:
int[] min = {Integer.MAX_VALUE}; // an int[] is used instead of int because the lambda
// expression cannot assign to a local variable
return s.chars() // get an IntStream of the characters of s
.map(this::mapToFactor) // map the characters to 1-4 or Integer.MAX_VALUE
.peek(i -> {if (i<min[0]) min[0]=i;}) // modify min to contain the current minimum
.filter(i->i==1) // keep only 1s
.findFirst() // get the first 1
.orElse(min[0]); // if 1 is not found, return min[0]
不是很优雅,但只会在找到第一个1
之前处理字符。