Java8 函数式接口之 Predicate与 BiPredicate

424 阅读3分钟

Predicate 怎么使用

  • 如何上手,先看示例代码,filter入参接收一个 Predicate 对象,用于判断过滤集合中的元素

    @Test
      public void test1() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    
        Predicate<Integer> predicate = integer -> integer % 2 == 0;
    
        List<Integer> collect = list.stream().filter(predicate).collect(Collectors.toList());
        // List<Integer> collect = list.stream().filter(integer -> integer % 2 == 0).collect(Collectors.toList());
    
    
        System.out.println(collect);
      }
    
  • 链路调用

  • 多条件过滤

    • && , ||
    • and(), or()
    • negate() 取反
    @Test
      public void test2() {
        List<String> list = Arrays.asList("A", "AA", "AAA", "B", "BB", "BBB");
    
        StringProcessor stringProcessor = new StringProcessor();
    
        System.out.println(stringProcessor.filter(
                list, x -> x.startsWith("A")));                    // [A, AA, AAA]
    
        System.out.println(stringProcessor.filter(
                list, x -> x.startsWith("A") && x.length() == 3)); // [AAA]
    
        Predicate<String> p1 = s -> s.startsWith("A");
        System.out.println(stringProcessor.filter(list, p1.negate()));
      }
    
      class StringProcessor {
    
        List<String> filter(List<String> list, Predicate<String> predicate) {
          return list.stream().filter(predicate::test).collect(Collectors.toList());
        }
      }
    
    
    
    // 链路调用
    @Test
      public void test3(){
        Predicate<String> startWithA = s -> s.startsWith("A");
        boolean result = startWithA.or(x -> x.startsWith("m")).test("mkyong");
        System.out.println(result);
    
      }
    
    
  • 与对象一起使用

    		public static List<Hosting> filterHosting(List<Hosting> hosting,
                                                  Predicate<Hosting> predicate) {
            return hosting.stream()
                    .filter(predicate)
                    .collect(Collectors.toList());
        }
        
        public static void main(String[] args) {
    
            Hosting h1 = new Hosting(1, "amazon", "aws.amazon.com");
            Hosting h2 = new Hosting(2, "linode", "linode.com");
            Hosting h3 = new Hosting(3, "liquidweb", "liquidweb.com");
            Hosting h4 = new Hosting(4, "google", "google.com");
    
            List<Hosting> list = Arrays.asList(new Hosting[]{h1, h2, h3, h4});
    
            List<Hosting> result = HostingRespository.filterHosting(list, x -> x.getName().startsWith("g"));
            System.out.println("result : " + result);  // google
    
            List<Hosting> result2 = HostingRespository.filterHosting(list, isDeveloperFriendly());
            System.out.println("result2 : " + result2); // linode
    
        }
    
        public static Predicate<Hosting> isDeveloperFriendly() {
            return n -> n.getName().equals("linode");
        }
    
    

Predicate 深入理解

  • 常见的 Api上面已经介绍了几个,现在进一步通过源码来学习下 Predicate 提供的 Api

    • and
    • test,接收一个参数并返回布尔值
    • or
    • negate
    @FunctionalInterface
    public interface Predicate<T> {
    
        /**
         * Evaluates this predicate on the given argument.
         *
         * @param t the input argument
         * @return {@code true} if the input argument matches the predicate,
         * otherwise {@code false}
         */
        boolean test(T t);
    
        /**
        与运算
         * Returns a composed predicate that represents a short-circuiting logical
         * AND of this predicate and another.  When evaluating the composed
         * predicate, if this predicate is {@code false}, then the {@code other}
         * predicate is not evaluated.
         *
         * <p>Any exceptions thrown during evaluation of either predicate are relayed
         * to the caller; if evaluation of this predicate throws an exception, the
         * {@code other} predicate will not be evaluated.
         *
         * @param other a predicate that will be logically-ANDed with this
         *              predicate
         * @return a composed predicate that represents the short-circuiting logical
         * AND of this predicate and the {@code other} predicate
         * @throws NullPointerException if other is null
         */
        default Predicate<T> and(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) && other.test(t);
        }
    
        /**
        	很好理解,取反
         * Returns a predicate that represents the logical negation of this
         * predicate.
         *
         * @return a predicate that represents the logical negation of this
         * predicate
         */
        default Predicate<T> negate() {
            return (t) -> !test(t);
        }
    
        /**
        或运算
         * Returns a composed predicate that represents a short-circuiting logical
         * OR of this predicate and another.  When evaluating the composed
         * predicate, if this predicate is {@code true}, then the {@code other}
         * predicate is not evaluated.
         *
         * <p>Any exceptions thrown during evaluation of either predicate are relayed
         * to the caller; if evaluation of this predicate throws an exception, the
         * {@code other} predicate will not be evaluated.
         *
         * @param other a predicate that will be logically-ORed with this
         *              predicate
         * @return a composed predicate that represents the short-circuiting logical
         * OR of this predicate and the {@code other} predicate
         * @throws NullPointerException if other is null
         */
        default Predicate<T> or(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) || other.test(t);
        }
    
        /**
         * Returns a predicate that tests if two arguments are equal according
         * to {@link Objects#equals(Object, Object)}.
         *
         * @param <T> the type of arguments to the predicate
         * @param targetRef the object reference with which to compare for equality,
         *               which may be {@code null}
         * @return a predicate that tests if two arguments are equal according
         * to {@link Objects#equals(Object, Object)}
         */
        static <T> Predicate<T> isEqual(Object targetRef) {
            return (null == targetRef)
                    ? Objects::isNull
                    : object -> targetRef.equals(object);
        }
    }
    
    

BiPredicate 怎么使用

  • 它比起 predicate 多了一个参数,可以联系 function 和 bifuncation 的关系

    public interface BiPredicate<T, U> {
        boolean test(T t, U u);
    }
    
  • 还是先看示例代码

      @Test
      public void test4() {
        BiPredicate<String, Integer> filter = (x, y) ->
                x.length() == y;
        ;
    
        boolean result = filter.test("mkyong", 6);
        System.out.println(result);  // true
    
        boolean result2 = filter.test("java", 10);
        System.out.println(result2); // false
      }
    
    
    
    	@Test
      public void testBiPredicate() {
    
        List<Domain> domains = Arrays.asList(new Domain("google.com", 1),
                new Domain("i-am-spammer.com", 10),
                new Domain("mkyong.com", 0),
                new Domain("microsoft.com", 2));
    
        BiPredicate<String, Integer> bi = (domain, score) -> {
          return (domain.equalsIgnoreCase("google.com") || score == 0);
        };
    
        System.out.println(filterBadDomain(domains, bi));
        // [PredicateTest.Domain(name=google.com, score=1), PredicateTest.Domain(name=mkyong.com, score=0)]
    
        
        List<Domain> result4 = filterBadDomain(domains, bi.or(
                (name, score) -> name.equalsIgnoreCase("microsoft.com") && score>2)
        );
        System.out.println(result4);
      }
    
    
      public static <T extends Domain> List<T> filterBadDomain(List<T> list, 
                                                               BiPredicate<String, Integer> biPredicate) {
    
        return list.stream()
                .filter(x -> biPredicate.test(x.getName(), x.getScore()))
                .collect(Collectors.toList());
    
      }
    
    
      @Data
      class Domain {
    
        String name;
        Integer score;
    
        public Domain(String name, Integer score) {
          this.name = name;
          this.score = score;
        }
        
      }
    
  • BiPredicate 的Api 跟 Predicate 没有太大差异,再此不在展开

    参考