2017-02-27 54 views
2

感謝您檢查我的問題!使用Java 8 Predicate查找「最」正確的值

我在使用包含多個謂詞並按特定順序應用的Streams時遇到了一些麻煩。

例如起見,請考慮以下IntPredicates:

 IntPredicate divisibleByThree = i -> i % 3 == 0; 
     IntPredicate divisibleByFour = i -> i % 4 == 0; 
     IntPredicate divisibleByFive = i -> i % 5 == 0; 
     IntPredicate divisibleByThreeAndFour = divisibleByThree.and(divisibleByFour); 
     IntPredicate divisibleByThreeAndFive = divisibleByThree.and(divisibleByFive); 
     IntPredicate divisibleByThreeAndFiveAndFour = divisibleByThreeAndFour.and(divisibleByFive); 
     //....arbitrary Number of predicates. 

第1部分

我已經轉換我已經下降到一個「FizzBu​​zz」 -esque版本的問題,試圖找到通過將特定順序的謂詞應用到流中來得到正確的答案。像這樣:

IntStream.range(1, 100).forEach(i -> { 
     //Order matters here! 
     if(divisibleByThreeAndFiveAndFour.test(i)){ 
      System.out.println("Three and four and five"); 
     } else if(divisibleByThreeAndFour.test(i)){ 
      System.out.println("Three and four"); 
     } else if(divisibleByThreeAndFive.test(i)){ 
      System.out.println("Three and four"); 
     } else if(divisibleByFive.test(i)){ 
      System.out.println("Five"); 
     } 
     //etc, etc. 
    }); 

我不認爲這是非常漂亮的代碼,有沒有更好的方式來實現這一目標?

第2部分

怎麼樣,如果我真的需要應用謂詞,看看是否合適的值出現在流,並計算相關的值返回(在這種情況下,一個字符串進行打印) 。那甚至看起來如何?

擬議天真的解決方案:

String bestValueFound = "None found"; 
if(IntStream.range(1, 100).filter(divisibleByThreeAndFiveAndFour).findFirst().isPresent()){ 
    bestValueFound = "Three and four and five"; 
} else if(IntStream.range(1, 100).filter(divisibleByThreeAndFour).findFirst().isPresent()){ 
    bestValueFound = "Three and four"; 
}else if(IntStream.range(1, 100).filter(divisibleByThreeAndFive).findFirst().isPresent()){ 
    bestValueFound = "Three and five"; 
} else if(IntStream.range(1, 100).filter(divisibleByThreeAndFive).findFirst().isPresent()){ 
    bestValueFound = "Five"; 
} 
System.out.println(bestValueFound); 

這似乎更糟,既美觀又因爲添加的迭代。

第3部分

難道這可能會被使用JavaSlang比賽更漂亮,更有效的方式解決了嗎?

//Note: Predicates needed to be changed from IntPredicate to Predicate<Integer> for correct compilation. 
Function<Integer, String> findString = i -> API.Match(i).of(
     Case(divisibleByThreeAndFiveAndFour, "Three and four and five"), 
     Case(divisibleByThreeAndFour, "Three and four"), 
     Case(divisibleByThreeAndFive, "Three and five"), 
     Case(divisibleByFive, "Fice"), 
     Case($(), "None found")); 
String bestValueFound = IntStream.range(1, 100).boxed().map(findString).findFirst().orElseThrow(() -> new RuntimeException("Something went wrong?")); 
System.out.println(bestValueFound); 

這裏的明顯的問題是「.findFirst()」,這將是整數1上這種情況下,使得整個表達式評估爲‘無發現’,然後終止。

我想要的是基本上抓住匹配我匹配中第一個謂詞的任何東西,並使用該值(如果存在),並且只給第二個Case匹配任何匹配,如果找不到第一個匹配,如果流中沒有值匹配任何謂詞,則只給予默認值(「未找到」)。

必須有更好的方法來做到這一點,對吧?或者我只是在浪費時間試圖做一些更好的事情,而不是更傳統,更強制的風格?

謝謝你閱讀我的問題!

+1

請只問1個問題。你有3個完全不同的問題。 – 4castle

+1

在第2部分中,您應該使用['anyMatch'](https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#anyMatch-java.util.function。 IntPredicate-)。 – 4castle

回答

7

您可以創建一個類來封裝謂詞和它的名字:

class NamedPredicate { 
    final String name; 
    final IntPredicate predicate; 

    NamedPredicate(String name, IntPredicate predicate) { 
     this.name = name; 
     this.predicate = predicate; 
    } 

    NamedPredicate and(NamedPredicate other) { 
     return new NamedPredicate(this.name + " and " + other.name, 
       this.predicate.and(other.predicate)); 
    } 
} 

and()方法允許我們撰寫他們類似於你做原始的方法:

NamedPredicate divisibleByThree = new NamedPredicate("three", i -> i % 3 == 0); 
NamedPredicate divisibleByFour = new NamedPredicate("four", i -> i % 4 == 0); 
NamedPredicate divisibleByFive = new NamedPredicate("five", i -> i % 5 == 0); 
NamedPredicate divisibleByThreeAndFour = divisibleByThree.and(divisibleByFour); 
NamedPredicate divisibleByThreeAndFive = divisibleByThree.and(divisibleByFive); 
NamedPredicate divisibleByThreeAndFiveAndFour = divisibleByThreeAndFour.and(divisibleByFive); 

現在我們可以通過它們按降序排列並打印第一個匹配謂詞的名稱,對於每個i

IntStream.range(1, 100) 
     .mapToObj(i -> Stream.of(
        divisibleByThreeAndFiveAndFour, 
        divisibleByThreeAndFour, 
        divisibleByThreeAndFive, 
        divisibleByFive, 
        divisibleByFour, 
        divisibleByThree) 
       .filter(p -> p.predicate.test(i)) 
       .findFirst() 
       .map(p -> p.name) 
       .orElse("none")) 
     .forEach(System.out::println); 
+0

不錯,但黑客一起這樣的名字是不會工作,如果你需要i18​​n –

+1

@PatrickParker足夠公平,但你可以很容易採取更明確的方法:'NamedPredicate和(NamedPredicate other,String newName){return new NamedPredicate (newName,this.predicate.and(other.predicate)); }' – shmosel

+0

爲什麼你沒有考慮一個divisibleByFourAndFive的情況? – sara

相關問題