2017-09-26 6 views
4

は、フィルタエレメントが記録された後に除外されるようにStream APIでこれ良い方法私は思ったんだけど

public static void main(String[] args) { 
    final List<String> dataCollection = Collections.emptyList(); 
    final Set<String> someValues = new HashSet<>(); 
    final Iterator<String> iterator = dataCollection.iterator(); 
    while (iterator.hasNext()) { 
     final String dataItem = iterator.next(); 
     // imagine some calculations 
     String calculatedData = dataItem; 
     if (!someValues.contains(calculatedData)) { 
      logger.error("Skipped data {} because of ...#1", dataItem); 
      iterator.remove(); 
      continue; 
     } 

     for (char element : dataItem.toCharArray()) { 
      // imagine some other calculations 
      if (element > 100) { 
       logger.error("Skipped data {} because of ...#2", dataItem); 
       iterator.remove(); 
       break; 
      } 
     } 
    } 
} 

のようなコードを書き換える方法があります。 peek()は、フィルタ前またはフィルタ後のすべての要素でアクションを実行するので、この場合は機能しません。

これまでのところ、ラムダの内部でロギングして設計していましたが、冗長で扱いにくく、同様の副作用があります。何らかの方法でコードをラップすることはできますが、コードを少し隠すだけです。

public static void main(String[] args) { 
    final List<String> dataCollection = Collections.emptyList(); 
    final Set<String> someValues = new HashSet<>(); 
    final Iterator<String> iterator = dataCollection.iterator(); 

    dataCollection.stream() 
      .filter(byCondition1(someValues)) 
      .filter(byCondition2()) 
      .collect(Collectors.toList()); 
} 

private static Predicate<String> byCondition1(Set<String> someValues) { 
    return dataItem -> { 
     final boolean remain = someValues.contains(dataItem); 
     if (!remain) { 
      logger.error("Skipped data {} because of ...#1", dataItem); 
     } 
     return remain; 
    }; 
} 

private static Predicate<String> byCondition2() { 
    return dataItem -> { 
     for (char element : dataItem.toCharArray()) { 
      // imagine some other calculations 
      if (element > 100) { 
       logger.error("Skipped data {} because of element {}...#2", dataItem, element); 
       return false; 
      } 
     } 
     return true; 
    }; 
} 

もっと良い方法があることを願っています。

+0

*おそらく* '述語'の拡張子は? 'LoggingPredicate extends Predicate ...'と静的メソッド 'static LoggingPredicate(Logger log)'のように、 'test'メソッドをオーバーライドしてオーバーライドします。ただのアイデア... – Eugene

+0

ええ、それは...このコードをラップする別の方法です。もちろん、それは私よりも再利用性が良い – WeGa

+0

Stream APIは 'peek'以外のログ用に何も持っていないので、それ以外の方法はないと思います – Eugene

答えて

3

臨時条件はそのスキップをされたと仮定すると、これは

List<String> dataCollection = Arrays.asList("FOO", "hello", "VALID", "123", "BAR", "bla"); 
Set<String> someValues = new HashSet<>(Arrays.asList("FOO", "BAR")); 

Predicate<String> firstPredicate = string -> !someValues.contains(string); 
Predicate<String> secondPredicate = string -> string.chars().noneMatch(c -> c>100); 

List<String> result; 

if(!logger.isLoggable(Level.WARNING)) { 
    result = dataCollection.stream() 
      .filter(firstPredicate) 
      .filter(secondPredicate) 
      .collect(Collectors.toList()); 
} 
else { 
    Map<Boolean, List<String>> map = dataCollection.stream() 
     .collect(Collectors.partitioningBy(firstPredicate)); 
    if(!map.get(false).isEmpty()) 
     logger.log(Level.WARNING, "Skipped data {0} because of ...#1", map.get(false)); 
    map = map.get(true).stream() 
     .collect(Collectors.partitioningBy(secondPredicate)); 
    if(!map.get(false).isEmpty()) 
     logger.log(Level.WARNING, "Skipped data {0} because of ...#2", map.get(false)); 
    result = map.get(true); 
} 

このコードは標準java.util.logging APIを使用して、指定したレベルのロギングが無効になっている場合は、ロギング関連の操作を実行しません実現可能です。一方、有効にしてスキップする項目に遭遇すると、要素ごとのエントリでログをフラッディングするのではなく、条件ごとにすべてを一度にレポートします。上に示したサンプルデータとデフォルトログハンドラを使用して、結果は、そのレベルのロギングが有効されているか否か[VALID, 123]、ある

Sep 26, 2017 11:00:46 AM LoggingExample main 
WARNUNG: Skipped data [FOO, BAR] because of ...#1 
Sep 26, 2017 11:00:46 AM LoggingExample main 
WARNUNG: Skipped data [hello, bla] because of ...#2 

プリント。

+0

2つのストリームを分割することをお勧めします。データフローを処理するための反応的な方法。悲しいことに、(少なくともjavaでは)分割は端末操作であり、ストリームの遅延評価の可能性が損なわれます。また、現在2つのコードブロックが実際にビジネスのalgorythm – WeGa

2

まず、OPのコードはそれほど悪くないと思います。第二に、我々は元のコードで行うことができます改善のビットが残っています:

final Predicate<String> loggingFilter = dataItem -> { 
    final String calculatedData = dataItem; // imagine some calculations 
    if (!someValues.contains(calculatedData)) { 
     logger.error("Skipped data {} because of ...#1", dataItem); 
     return false; 
    }   
    final OptionalInt element = dataItem.chars().filter(ch -> ch > 100).findAny(); 
    if (element.isPresent()) { 
     logger.error("Skipped data {} because of element {}...#2", dataItem, element.getAsInt()); 
     return false; 
    } 
    return true; 
}; 

dataCollection.stream().filter(loggingFilter).collect(Collectors.toList()); 

私はそれはだかはわからない:あなたや他の誰に「冗長ぎこちないなどの副作用」。私には、重複したコードや副作用はありません。それはほとんどの言語で可能ですが、一部の言語ではチェーン操作APIを提供してif(...); if(...)を削除する場合があります。

関連する問題