java 8 stream finde MIN / MAX限制

时间:2016-11-04 08:12:52

标签: java java-8 java-stream

例如,有限量的元素的最小代码是最小的:

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代码?

3 个答案:

答案 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之前处理字符。