2016-11-04 9 views
2

要素の制限された量からfinde最低ARAコード:1,2,3,4は、Integer.MAX_VALUE:の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に直面したとき、将来の反復をスキップして結果を返すことができます。私たちの文字列が大きな警戒が、我々は1が大幅にパフォーマンスダウンスキップiterrationsで代わりのJava 7スタイルのJavaの8ストリームを使用していすることができますのであれば上の場合

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 7コードをJava 8ストリーム形式で記述する方法を教えてください。

+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

1の最初のオカレンスを検索するStreamパイプラインを実行できます。その問題は、1が見つからない場合は、別のStreamパイプラインを実行して最小値を見つけなければならないことです。

私はpeekと現在の最小を維持しながら、最初の1探しStreamパイプラインを実行していると考えることができます別の方法:

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'文を追加すると、最初の1が見つかるまで要素を処理するだけです。状態変数に関して、私はそれほどエレガントではないと言った。おそらく、あなたは何か良いものを考えるでしょう。 – Eran

+0

しかし、それはJava 7と同じではありません。なぜなら、1)最初に私たちはすべてのストリームに対してi == 1を除外します。2)見つからなければ、最小値を見つけるためにもう1回繰り返します。これは、大きな文字列 '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オプティマイザは、コードのインライン化と解析を行った後、ストリームの内部ループに早期終了条件を追加することも可能です。

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