Java函数式编程之Predicate 过滤操作

使用Java8新增的Predicate操作集合

Java 8 中 Collection集合新增了一些需要Predicate参数的方法,这些方法可以对集合元素进行过滤。程序可使用Lambda表达式构建Predicate对象。

描述:

Predicate< T>接口接受一个T类型参数,返回一个boolean值。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):

Predicate<String> predicate = (s) -> s.length() > 0;
 
predicate.test("foo");              // true
predicate.negate().test("foo");     // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();

查看源码

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
    
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

查找对象

该代码完成了以下4个需求

  • 统计书名中出现“疯狂”字符串的图书数量。

  • 统计书名中出现“Java”字符串图书数量。

  • 统计书名长度大于10的图书数量。

  • 删除书名长度小于10的图书。

Predicate是一种谓词动作接口:是一个函数式接口,即表示一个动作

实现如下

import java.util.*;
import java.util.function.*;
  
public class PredicateTest {
    public static void main(String[] args) {
    
        // 创建books集合、为books集合添加元素的代码与前一个程序相同。
        Collection books = new HashSet();
        books.add(new String("轻量级Java EE企业应用实战"));
        books.add(new String("疯狂Java讲义"));
        books.add(new String("疯狂iOS讲义"));
        books.add(new String("疯狂Ajax讲义"));
        books.add(new String("疯狂Android讲义"));
        
        // 统计书名包含“疯狂”子串的图书数量
        System.out.println(calAll(books , ele->((String)ele).contains("疯狂")));
        // 统计书名包含“Java”子串的图书数量
        System.out.println(calAll(books , ele->((String)ele).contains("Java")));
        // 统计书名字符串长度大于10的图书数量
        System.out.println(calAll(books , ele->((String)ele).length() > 10));
          
        // 使用Lambda表达式(目标类型是Predicate)过滤集合  
        books.removeIf(ele -> ((String)ele).length() < 10);
        
        System.out.println(books);
    }
    
    public static int calAll(Collection books , Predicate p) {  
        int total = 0;
        for (Object obj : books) {
            // 使用Predicate的test()方法判断该对象是否满足Predicate指定的条件  
            if (p.test(obj)) {
                total ++;
            }
        }
        return total;
    }
}

运行结果

4
2
2
[疯狂Android讲义, 轻量级Java EE企业应用实战]

删除集合元素

Java 8 中 Collection集合中新增了一个removeIf(Predicate filter) 方法,该方法将会批量删除符合filter条件的所有元素。

方法:removeIf(Predicate<? super E> filter)

如上述代码中,删除书名长度小于10的图书 功能,这里追加一个 删除所有包含Java的元素

books.removeIf(o -> {
    if (o.toString().contains("Java")){
            return true;
        }
        return false;
});

// 以上代码简写
books.removeIf(o ->  o.toString().contains("Java"));

books.forEach(System.out::println);

运行结果

疯狂Ajax讲义
疯狂Android讲义
疯狂iOS讲义

组合谓词

Java 8定义了许多有用的java.util.function包中接口的组合接口,但这种组合并不都是一样的。所有的谓语的变体(《DoublePredicateIntPredicateLongPredicate和Predicate<T>)都定义了相同的组合与修改方法:and(),negate()和or()。但是Function<T>的基本数据类型变体就没有定义任何组合与修改方法。如果你拥有使用函数式编程语言的经验,那么你可能就发会发现这些不同之处和奇怪的忽略。

组合条件示例

Predicate<String> pred1 = name -> name.contains("Java");
Predicate<String> pred2 = name -> name.contains("IOS");
books.removeIf(pred1.or(pred2));

源码分析

下边是JDK1.8.0_121中接口Collection的部分源码:

/**
  * Removes all of the elements of this collection that satisfy the given
  * predicate.  Errors or runtime exceptions thrown during iteration or by
  * the predicate are relayed to the caller.
  *
  * @implSpec
  * The default implementation traverses all elements of the collection using
  * its {@link #iterator}.  Each matching element is removed using
  * {@link Iterator#remove()}.  If the collection's iterator does not
  * support removal then an {@code UnsupportedOperationException} will be
  * thrown on the first matching element.
  *
  * @param filter a predicate which returns {@code true} for elements to be
  *        removed
  * @return {@code true} if any elements were removed
  * @throws NullPointerException if the specified filter is null
  * @throws UnsupportedOperationException if elements cannot be removed
  *         from this collection.  Implementations may throw this exception if a
  *         matching element cannot be removed or if, in general, removal is not
  *         supported.
  * @since 1.8
  */
default boolean removeIf(Predicate<? super E> filter) {
     Objects.requireNonNull(filter);
     boolean removed = false;
     final Iterator<E> each = iterator();
     while (each.hasNext()) {
         if (filter.test(each.next())) {
             each.remove();
             removed = true;
         }
     }
     return removed;
}

下边是翻译后的源码(英语渣,如有错误,请指正):

/**
  * 移除集合中满足给定条件的所有元素,错误或者运行时异常发生在迭代时或者把条件传递给调用者的时候。
  *
  * @implSpec
  * 默认的实现贯穿了使用迭代器iterator的集合的所有元素。每一个匹配的元素都将被用Iterator接口中的
  * remove()方法移除。如果集合的迭代器不支持移除,则在第一次匹配时就会抛出异常 UnsupportedOperationException
  *
  * @param filter 令元素移除成功的条件
  * @return {@code true} 如果所有的元素都被移除
  * @throws NullPointerException 如果有一个过滤器是空的
  * @throws UnsupportedOperationException 如果元素不能被从该集合中移除。如果一个匹配元素不能被移除,
  *         通常来说,它就不支持移除操作,这时可能抛出这个异常。
  * @since 1.8
  */
 default boolean removeIf(Predicate<? super E> filter) {
     Objects.requireNonNull(filter);
     boolean removed = false;
     final Iterator<E> each = iterator();
     while (each.hasNext()) {
         if (filter.test(each.next())) {
             each.remove();
             removed = true;
         }
     }
     return removed;
 }



赞(52) 打赏
未经允许不得转载:优客志 » JAVA开发
分享到:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏