使用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包中接口的组合接口,但这种组合并不都是一样的。所有的谓语的变体(《DoublePredicate
,IntPredicate
,LongPredicate
和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; }