Java Predicate
上次修改于 2025 年 5 月 28 日
在本文中,我们将展示如何在 Java 中使用谓词。使用谓词,我们可以创建更简洁易读的代码。谓词还有助于创建更好的测试。
谓词定义
谓词 的一般含义是指关于某事物的陈述,该事物要么为真,要么为假。在编程中,谓词表示返回布尔值的单参数函数。
Predicate 接口
在 Java 中,谓词使用函数式接口来实现。 Predicate<T>
接口是一个泛型函数式接口,表示一个返回布尔值的单参数函数。它位于 java.util.function
包中,包含 test(T t)
方法,该方法根据指定的条件评估给定的参数。
与某些编程语言不同,Java 不支持独立函数。此外,Java 中的方法不是一等公民——它们不能存储在集合中或直接作为参数传递。为了支持函数式编程,Java 依赖于接口,允许对象表示函数并传递给诸如 Iterables.filter
之类的方法。借助 Java lambda 表达式,使用谓词变得更加简洁和直观。
Java Predicate 示例
以下示例创建了一个简单的 Java Predicate。
class BiggerThanFive<E> implements Predicate<Integer> { @Override public boolean test(Integer v) { Integer five = 5; return v > five; } } void main() { List<Integer> nums = List.of(2, 3, 1, 5, 6, 7, 8, 9, 12); BiggerThanFive<Integer> btf = new BiggerThanFive<>(); nums.stream().filter(btf).forEach(System.out::println); }
在此示例中,谓词用于过滤整数。
class BiggerThanFive<E> implements Predicate<Integer> { @Override public boolean test(Integer v) { Integer five = 5; return v > five; } }
这是一个实现 Predicate<Integer>
接口的 Java 类。 其 test
方法对于大于 5 的值返回 true。
List<Integer> nums = List.of(2, 3, 1, 5, 6, 7, 8, 9, 12);
我们有一个整数值列表。
BiggerThanFive<Integer> btf = new BiggerThanFive<>();
实例化一个 BiggerThanFive
。
nums.stream().filter(btf).forEach(System.out::println);
谓词对象被传递到 filter
方法,以获取列表中所有大于 5 的值。
$ java Main.java 6 7 8 9 12
带有 lambda 的 Java Predicate
Java lambda 表达式简化了 Java Predicate 的创建。
void main() { List<Integer> nums = List.of(2, 3, 1, 5, 6, 7, 8, 9, 12); Predicate<Integer> btf = n -> n > 5; nums.stream().filter(btf).forEach(System.out::println); }
该示例过滤整数值;这次我们使用 Java lambda 表达式,这使得代码更短。
Predicate<Integer> btf = n -> n > 5;
这是一个创建谓词的单行代码。
ArrayList removeIf 方法
ArrayList
的 removeIf
方法移除所有满足给定谓词的元素。
void main() { var words = new ArrayList<String>(); words.add("sky"); words.add("warm"); words.add("winter"); words.add("cloud"); words.add("pen"); words.add("den"); words.add("tree"); words.add("sun"); words.add("silk"); Predicate<String> hasThreeChars = word -> word.length() == 3; words.removeIf(hasThreeChars); System.out.println(words); }
我们有一个单词列表。我们从列表中删除所有具有三个拉丁字符的单词。
$ java Main.java [warm, winter, cloud, tree, silk]
Collectors.PartitioningBy 方法
Collectors.PartitioningBy
返回一个 Collector
,它根据 Predicate
对输入元素进行分区,并将它们组织成 Map<Boolean, List<T>>。
void main() { var values = List.of(3, -1, 2, 4, -1, 1, 2, 3); Predicate<Integer> isPositive = e -> e > 0; Map<Boolean, List<Integer>> groups = values.stream() .collect(Collectors.partitioningBy(isPositive)); System.out.println(groups.get(true)); System.out.println(groups.get(false)); List<List<Integer>> subSets = new ArrayList<>(groups.values()); System.out.println(subSets); }
我们有一个整数列表。该列表被划分为两个子列表:正值和负值。
$ java Main.java [4, 1, 2, 3] [-3, -1, -2, -1] [[-3, -1, -2, -1], [4, 1, 2, 3]]
Pattern.asMatchPredicate
Pattern.asMatchPredicate
创建一个谓词,用于测试模式是否与给定的输入字符串匹配。
void main() { var words = List.of("book", "bookshelf", "bookworm", "bookcase", "bookish", "bookkeeper", "booklet", "bookmark"); var pred = Pattern.compile("book(worm|mark|keeper)?").asMatchPredicate(); words.stream().filter(pred).forEach(System.out::println); }
我们使用 Pattern.asMatchPredicate
从正则表达式模式创建谓词,并将其应用于 filter
方法。
$ java Main.java book bookworm bookkeeper bookmark
Stream.allMatch 谓词
Stream.allMatch
方法返回一个布尔值,指示流的所有元素是否都与提供的谓词匹配。
void main() { var values1 = List.of(1, 5, 3, 2, 8, 6, 7); var values2 = List.of(1, 5, 3, -2, 8, 0, 9); Predicate<Integer> isPositive = e -> e > 0; var res1 = values1.stream().allMatch(isPositive); if (res1) { System.out.println("All values of collection values1 are positive"); } else { System.out.println("All values of collection values1 are not positive"); } var res2 = values2.stream().allMatch(isPositive); if (res2) { System.out.println("All values of collection values2 are positive"); } else { System.out.println("All values of collection values2 are not positive"); } }
在该示例中,我们检查两个集合的所有值是否都只有正值。
$ java Main.java All values of collection values1 are positive All values of collection values2 are not positive
Pattern.asPredicate
Pattern.asPredicate
方法创建一个谓词,用于测试是否在给定的输入字符串中找到了此模式。 Stream.AnyMatch
方法返回一个布尔值,指示流的任何元素是否与提供的谓词匹配。
void main() { var words = List.of("skylark", "trial", "water", "cloud", "curtain", "falcon"); var pred = Pattern.compile("^...{3}$").asPredicate(); var res = words.stream().anyMatch(pred); if (res) { System.out.println("There is a word which has three latin characters"); } else { System.out.println("There is no word which has three latin characters"); } }
我们有一个单词列表。我们检查是否有一个单词具有三个拉丁字符。
$ java Main.java There is a word which has three latin characters
Stream.Iterate 方法
Stream.Iterate
方法返回一个顺序有序的流,该流通过将给定的函数迭代地应用于初始元素来生成,并以满足给定的谓词为条件。
void main() { Predicate<Double> pred = e -> e < 100; UnaryOperator<Double> op = e -> e * 2; Stream.iterate(1d, pred, op).forEach(System.out::println); }
使用 Stream.iterate
,我们生成一个流,该流将给定的函数应用于先前生成的元素。当谓词返回 false 时,流终止;在我们的例子中,当生成的流值大于 100 时。
$ java Main.java 1.0 2.0 4.0 8.0 16.0 32.0 64.0
具有多个条件的谓词
以下示例使用带有两个条件的谓词。
void main() { var countries = List.of( new Country("Iran", 80840713), new Country("Hungary", 9845000), new Country("Poland", 38485000), new Country("India", 1342512000), new Country("Latvia", 1978000), new Country("Vietnam", 95261000), new Country("Sweden", 9967000), new Country("Iceland", 337600), new Country("Israel", 8622000)); Predicate<Country> p1 = c -> c.name().startsWith("I") && c.population() > 10000000; countries.stream().filter(p1).forEach(System.out::println); } record Country(String name, int population) { }
在此示例中,我们创建一个国家/地区列表。我们按国家/地区名称和人口过滤该列表。
Predicate<Country> p1 = c -> c.name().startsWith("I") && c.population() > 10000000;
对于以“I”开头的国家/地区且人口超过一千万的国家/地区,该谓词返回 true。
$ java Main.java Country{name=Iran, population=80840713} Country{name=India, population=1342512000}
列表中有两个国家/地区满足条件:伊朗和印度。
Predicate.isEqual 方法
Predicate.isEqual
返回一个谓词,该谓词根据 Objects.equals
测试两个参数是否相等。
void main() { var users1 = List.of(new User("John Doe", "gardener"), new User("Roger Roe", "driver"), new User("Jane Doe", "teacher")); var users2 = List.of(new User("John Doe", "gardener"), new User("Roger Roe", "driver"), new User("Jane Doe", "teacher")); var users3 = List.of(new User("John Doe", "architect"), new User("Roger Roe", "driver"), new User("Jane Doe", "teacher")); Predicate<List<User>> pred = Predicate.isEqual(users1); if (pred.test(users2)) { System.out.println("users1 and user2 are equal"); } else { System.out.println("users1 and user2 are not equal"); } if (pred.test(users3)) { System.out.println("users1 and user3 are equal"); } else { System.out.println("users1 and user3 are not equal"); } } record User(String name, String occupation) { }
该示例检查两个用户列表是否相等。
$ java Main.java users1 and user2 are equal users1 and user3 are not equal
IntPredicate
IntPredicate
表示一个以 int 值作为参数的谓词。 这是 Predicate<E>
的消耗 int 的原始类型特化。
void main() { int[] nums = { 2, 3, 1, 5, 6, 7, 8, 9, 12 }; IntPredicate p = n -> n > 5; Arrays.stream(nums).filter(p).forEach(System.out::println); }
该示例使用 filter
和 IntPredicate
过滤 int
值数组。
int nums[] = { 2, 3, 1, 5, 6, 7, 8, 9, 12 };
我们定义一个整数数组。
IntPredicate p = n -> n > 5;
创建一个 IntPredicate
;对于大于 5 的 int
值,它返回 true。
Arrays.stream(nums).filter(p).forEach(System.out::println);
我们从数组创建一个流并过滤元素。 filter
方法接收谓词作为参数。
BiPredicate
BiPredicate
是 Predicate
的双元特化。 它表示两个参数的谓词。
void main() { var words = List.of("sky", "water", "club", "spy", "silk", "summer", "war", "cup", "cloud", "coin", "small", "terse", "falcon", "snow", "snail", "see"); BiPredicate<String, Integer> pred = (w, len) -> w.length() == len; words.stream().filter(e -> pred.test(e, 3)).forEach(System.out::println); System.out.println("---------------------"); words.stream().filter(e -> pred.test(e, 4)).forEach(System.out::println); }
BiPredicate
用于挑选具有三个和四个拉丁字符的单词。
$ java Main.java sky spy war cup see --------------------- club silk coin snow
组合谓词
借助 and
和 or
方法,我们可以在 Java 中组合谓词。
void main() { int[] nums = {2, 3, 1, 5, 6, 7, 8, 9, 12}; IntPredicate p1 = n -> n > 3; IntPredicate p2 = n -> n < 9; Arrays.stream(nums).filter(p1.and(p2)).forEach(System.out::println); System.out.println("-------------------"); IntPredicate p3 = n -> n == 6; IntPredicate p4 = n -> n == 9; Arrays.stream(nums).filter(p3.or(p4)).forEach(System.out::println); }
该示例使用 IntPredicates
的组合过滤数据。
IntPredicate p1 = n -> n > 3; IntPredicate p2 = n -> n < 9; Arrays.stream(nums).filter(p1.and(p2)).forEach(System.out::println);
我们使用 and
方法组合两个谓词;我们得到大于 3 且小于 9 的整数。
IntPredicate p3 = n -> n == 6; IntPredicate p4 = n -> n == 9; Arrays.stream(nums).filter(p3.or(p4)).forEach(System.out::println);
使用 or
方法,我们获得等于 6 或 9 的值。
$ java Main.java 5 6 7 8 ------------------- 6 9
应用谓词列表
在以下示例中,我们使用谓词列表。
void main() { var words = List.of("sky", "curtain", "sin", "shy", "way", "club", "spy", "silk", "summer", "war", "cup", "cloud", "coin", "small", "set", "terse", "tree", "sea", "sip", "snow", "snail", "sly", "six", "sod", "see", "sit", "sad", "wry", "why"); Predicate<String> p1 = e -> e.startsWith("s") || e.startsWith("w"); Predicate<String> p2 = e -> e.endsWith("y"); Predicate<String> p3 = e -> e.length() == 3; var prs = List.of(p1, p2, p3); var result = words.stream() .filter(prs.stream().reduce(x -> true, Predicate::and)) .collect(Collectors.toList()); result.forEach(System.out::println); }
借助 reduce
方法,我们将谓词列表应用于单词列表。
$ java Main.java sky shy way spy sly wry why
具有方法引用的谓词
可以使用方法引用轻松创建谓词。这些是使用 ::
运算符创建的。
void main() { var words = List.of("sky", "", "club", "spy", "silk", "summer", "war", "cup", "cloud", "coin", "small", "terse", "", "snow", "snail", "see"); Predicate<String> pred = String::isEmpty; var res = words.stream().anyMatch(pred); if (res) { System.out.println("There is an empty string"); } else { System.out.println("There is no empty string"); } }
该示例检查列表中是否有任何空字符串。
否定谓词
negate
方法返回一个表示给定谓词的逻辑否定的谓词。
void main() { int[] nums = {2, 3, 1, 5, 6, 7, 8, 9, 12}; IntPredicate p = n -> n > 5; Arrays.stream(nums).filter(p).forEach(System.out::println); System.out.println("-----------------"); Arrays.stream(nums).filter(p.negate()).forEach(System.out::println); }
该示例演示了 negate
方法的用法。
IntPredicate p = n -> n > 5;
我们有一个对于大于 5 的值返回 true 的谓词。
Arrays.stream(nums).filter(p).forEach(System.out::println);
我们过滤所有大于 5 的整数。
Arrays.stream(nums).filter(p.negate()).forEach(System.out::println);
借助 negate
方法,我们得到相反的结果:小于或等于 4 的值。
$ java Main.java 6 7 8 9 12 ----------------- 2 3 1 5
或者,我们可以使用 Predicate.not
方法。
void main() { var words = List.of("book", "cup", "tree", "town", "sky", "by", "call", "ten", "top", "smart", "park"); Predicate<String> hasThreeChars = (String word) -> word.length() == 3; var res = words.stream().filter(hasThreeChars).toList(); System.out.println(res); var res2 = words.stream().filter(Predicate.not(hasThreeChars)).toList(); System.out.println(res2); }
在该程序中,我们定义一个单词列表。定义的谓词对于所有包含三个拉丁字符的单词返回 true。 Predicate.not(hasThreeChars)
返回相反的结果:所有具有较少或更多拉丁字符的单词。
$ java Main.java [cup, sky, ten, top] [book, tree, town, call, smart, park]
谓词作为方法参数
谓词可以作为方法参数传递。
void main() { List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); List<Integer> all = eval(list, n -> true); System.out.println(all); List<Integer> evenValues = eval(list, n -> n % 2 == 0); System.out.println(evenValues); List<Integer> greaterThanSix = eval(list, n -> n > 6); System.out.println(greaterThanSix); } List<Integer> eval(List<Integer> values, Predicate<Integer> predicate) { return values.stream().filter(predicate) .collect(Collectors.toList()); }
在此示例中,我们将谓词函数作为第二个参数传递给 eval
方法。
来源
在本文中,我们展示了如何在 Java 中使用谓词。 我们演示了如何创建谓词、如何在流中使用它们以及如何组合它们。 我们还展示了如何将谓词与 lambda 表达式和方法引用一起使用。 谓词是 Java 中的一个强大工具,可以帮助我们编写更简洁易读的代码。
作者
列出所有Java教程。