2016-11-04 55 views
2

例如有ARA代码用于从元件的限制量FINDE最小:java的8个流FINDE MIN/MAX限制

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; 
    } 
} 

完全以只存在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; 
    } 
} 

于是就如果我们的字符串将警惕大,那么我们就可以显著下跌性能通过使用Java 8个流而不是Java 7风格的跳跃iterrations如果1中找到。

您能否介绍一下如何在java 8流风格中编写上述Java 7代码?

+0

的可能的复制[上的流如何短路一个减少()操作?](http://stackoverflow.com/questions/32495069/how-to-short-circuit-a-reduce-流操作) – user140547

+0

[通过谓词限制流]可能的重复(http://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate) – the8472

回答

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发现处理的字符。

+0

.peek(i - > {if(i Alstresh

+2

@Alstresh'peek'不会在所有元素上运行。你可以在'peek'中添加一个'println'语句来查看它只处理元素,直到找到第一个元素。关于状态变量,我确实说它不是很优雅。也许你会想到更好的东西。 – Eran

+0

但是,它与java 7不一样,因为1)首先,我们在所有流中使用ifde == 1 2)如果没有找到,那么迭代另一次以获得最小值。它显着增加了大字符串中'A'的复杂度。 – Alstresh

2

这是一个典型的过早优化案例。如果你关心绩效,短暂迭代是最后一件事,你应该担心。

让我们看一下你的Java 7的变体:

for (Character ch : s.toCharArray()) { 

之前,你甚至开始你的迭代,你在呼唤String.toCharArray(),这在新分配char[]对象创建的String内容的副本。当然,要创建该副本,实现必须迭代整个String。在你自己的迭代开始之前。

然后,您将每个char值装箱到一个Character对象中。由于不可识别的原因,因为您的mapToFactor方法预计值为int,所以Character对象必须在此处取消装箱。

由于这些原因,在大多数环境中,s.chars().map(this::mapToFactor).min().getAsInt()可能比您的Java 7变体对大字符串要快得多。尤其是,当我们考虑具有A,即达到最小值1并且能够提前退出时,情况并非总是如此。

通常,您应该测量实际执行时间,而不是猜测特定方法的假定缺陷。只有在遇到实际性能问题时才开始尝试优化。由于您对创建String的完整副本的原始代码感到满意,因此您应该对Stream版本感到满意,并且不需要这些不必要的副本。在内联和分析代码之后,HotSpot优化器甚至可能会向Stream的内部循环添加提前终止条件。

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(); 
}