Java Predicate接口,用户希望使用 java.util.function.Predicate
接口筛选数据,可以使用 lambda 表达式或方法引用来实现 boolean test(T t)
方法。Predicate
接口主要用于流的筛选。给定一个包含若干项的流,Stream
接口的 filter
方法传入 Predicate
并返回一个新的流,它仅包含满足给定谓词的项。
Java Predicate接口 问题描述
用户希望使用 java.util.function.Predicate
接口筛选数据。
Java Predicate接口 解决方案
使用 lambda 表达式或方法引用来实现 boolean test(T t)
方法。
Java Predicate接口 具体实例
Predicate
接口主要用于流的筛选。给定一个包含若干项的流,Stream
接口的 filter
方法传入 Predicate
并返回一个新的流,它仅包含满足给定谓词的项。
Predicate
接口包含的单一抽象方法为 boolean test(T t)
,它传入一个泛型参数并返回 true
或 false
。例 2-6 列出了 Predicate
接口定义的所有方法(包括静态和默认方法)。
例 2-6
Predicate
接口定义的方法
default Predicate<T> and(Predicate<? super T> other)
static <T> Predicate<T> isEqual(Object targetRef)
default Predicate<T> negate()
default Predicate<T> or(Predicate<? super T> other)
boolean test(T t) ➊
➊ 单一抽象方法
给定一个名称集合,可以通过流处理找出所有具有特定长度的实例,如例 2-7 所示。
例 2-7 查找具有给定长度的字符串
public String getNamesOfLength(int length, String... names) {
return Arrays.stream(names)
.filter(s -> s.length() == length) ➊
.collect(Collectors.joining(", "));
}
➊ 满足给定长度字符串的谓词
或者,我们也可能只需要返回以特定字符串开头的名称,如例 2-8 所示。
例 2-8 查找以给定字符串开头的字符串
public String getNamesStartingWith(String s, String... names) {
return Arrays.stream(names)
.filter(str -> str.startsWith(s)) ➊
.collect(Collectors.joining(", "));
}
➊ 返回以给定字符串开头的字符串
如果允许客户端指定条件,Predicate
的通用性会更强。例 2-9 显示了其中一种应用。
例 2-9 查找满足任意谓词的字符串
public class ImplementPredicate {
public String getNamesSatisfyingCondition(
Predicate<String> condition, String... names) {
return Arrays.stream(names)
.filter(condition) ➊
.collect(Collectors.joining(", "));
}
}
// 其他方法
}
➊ 根据提供的谓词进行筛选
上述用法相当灵活,但依靠客户端自己编写所有谓词或许不太容易。一种方案是将常量添加到代表最常见情况的类中,如例 2-10 所示。
例 2-10 为常见情况添加常量
public class ImplementPredicate {
public static final Predicate<String> LENGTH_FIVE = s -> s.length() == 5;
public static final Predicate<String> STARTS_WITH_S =
s -> s.startsWith("S");
// 其余代码和之前一样
}
提供谓词作为参数的另一个优点是,可以使用默认方法 and
、or
与 negate
,并根据一系列单个元素来创建复合谓词(composite predicate)。
例 2-11 的测试用例展示了各种方法的应用。
import static functionpackage.ImplementPredicate.*; ➊
import static org.junit.Assert.assertEquals;
// 其他导入
public class ImplementPredicateTest {
private ImplementPredicate demo = new ImplementPredicate();
private String[] names;
@Before
public void setUp() {
names = Stream.of("Mal", "Wash", "Kaylee", "Inara", "Zoë",
"Jayne", "Simon", "River", "Shepherd Book")
.sorted()
.toArray(String[]::new);
}
@Test
public void getNamesOfLength5() throws Exception {
assertEquals("Inara, Jayne, River, Simon",
demo.getNamesOfLength(5, names));
}
@Test
public void getNamesStartingWithS() throws Exception {
assertEquals("Shepherd Book, Simon",
demo.getNamesStartingWith("S", names));
}
@Test
public void getNamesSatisfyingCondition() throws Exception {
assertEquals("Inara, Jayne, River, Simon",
demo.getNamesSatisfyingCondition(s -> s.length() == 5, names));
assertEquals("Shepherd Book, Simon",
demo.getNamesSatisfyingCondition(s -> s.startsWith("S"),
names));
assertEquals("Inara, Jayne, River, Simon",
demo.getNamesSatisfyingCondition(LENGTH_FIVE, names));
assertEquals("Shepherd Book, Simon",
demo.getNamesSatisfyingCondition(STARTS_WITH_S, names));
}
@Test
public void composedPredicate() throws Exception {
assertEquals("Simon",
demo.getNamesSatisfyingCondition(
LENGTH_FIVE.and(STARTS_WITH_S), names)); ➋
assertEquals("Inara, Jayne, River, Shepherd Book, Simon",
demo.getNamesSatisfyingCondition(
LENGTH_FIVE.or(STARTS_WITH_S), names)); ➋
assertEquals("Kaylee, Mal, Shepherd Book, Wash, Zoë",
demo.getNamesSatisfyingCondition(LENGTH_FIVE.negate(), names)); ➌
}
}
❶ 静态导入让使用常量更加简单
❷ 复合
❸ 否定
标准库还支持 Predicate
接口的其他一些用法。
Optional.filter(Predicate<? super T> predicate)
如果值存在且匹配某个给定的谓词,则返回描述该值的 Optional
,否则返回一个空 Optional
。
Collection.removeIf(Predicate<? super E> filter)
删除集合中所有满足谓词的元素。
Stream.allMatch(Predicate<? super T> predicate)
如果流的所有元素均满足给定的谓词,则返回 true
。anyMatch
和 noneMatch
方法的用法与之类似。
Collectors.partitioningBy(Predicate<? super T> predicate)
返回一个 Collector
,它将流分为两类(满足谓词和不满足谓词)。
只要流仅返回特定的元素,Predicate
接口就很有用。希望本范例能对读者有所启发,理解这种接口的用法。