2017-02-27 42 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