最近学习PAIP的时候,顺便看了下回味了下Common Lisp,注意到几个实用的集合操作函数,本文记录下在Java中的实现。
场景
我想大家在平常开发中应该经常遇到这样场景:想要从集合中找到符合某个条件的元素,或者判断集合中是否存在某种数据,又或者是判断集合中的所有元素都符合某个要求。
针对第一种问题的基本写法是for-if-break:
Image thisImage;
for (Image image : list) {
if (image.getId() == id) {
thisImage = image;
break;
}
}
//继续使用thisImage
第二种也是类似:
boolean containsPNG = false;
for (Image image : list) {
if (image.getType() == PNG) {
containsPNG = true;
break;
}
}
//根据containsPNG进行下面操作
第三种则是:
boolean isAllPNG = true;
for (Image image : list) {
if (image.getId() != PNG) {
isAllPng = false;
break;
}
}
//根据isAllPNG进行下面操作
这几段都是简单代码,但是一个项目中可能出现多次,而且这三种情况代码非常相似,只有细节不同,但是表达的逻辑还是有很大区别,并不直白(要从代码中看出逻辑还是得花个5-10秒),没法一眼在3秒内得到代码意义。
正好Common Lisp规范中包含专门处理这类问题的标准函数,下面就来介绍下。
代码示例
首先是应对第一种情况的,我们想要找到集合中某个符合条件的元素,那么就有操作some
:
public void testSome() {
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
some(nums, this::isOdd).ifPresent(oddInt -> {
//list中存在某个基数
});
some(images, image -> image.getId() == 0).ifPresent(invalidImage -> {
//images中存在id为0的非法图片
});
}
private boolean isOdd(Integer i) {
return i % 2 == 1;
}
处理第二种“是否包含任何符合某条件的元素”情况的操作是any
:
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
boolean containsEvenNum = any(nums, i -> i % 2 == 0);//包含偶数
boolean containsPNG = any(images, image -> image.getType() == PNG);//图片列表中包含PNG
boolean notContainsNegativeNum = notAny(nums, i -> i < 0);//不包含任何负数
处理三种“是否所有元素都符合某个条件”情况的操作是every
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
List<String> strings = Arrays.asList("", "asdf", "1234");
boolean allNonNull = every(strings, Objects::nonNull);//所有字符串都不是null
boolean allIsPositive = every(nums, i -> i > 0);//所有数字都是整数
boolean notAllStringIsDigits = notEvery(strings, TextUtils::isDigitsOnly);//不是所有的字符串都只有数字
工具实现
some
实现如下:
public static <T> Optional<T> some(Iterable<T> iterable, Predicate<? super T> predicate) {
if (iterable != null) {
for (T t : iterable) {
if (predicate.test(t)) {
return Optional.of(t);
}
}
}
return Optional.empty();
}
这里的predicate
是一个断言,用来检查数据是否符合要求,比如“是偶数”就是i -> i % 2 == 0
,“正整数”就是i -> i > 0
。返回值是Optional
形式,详细用法见相关文档。
any
的实现是:
public static <T> boolean any(Iterable<T> iterable, Predicate<? super T> predicate) {
return some(iterable, predicate).isPresent();
}
因为“是否包含任何符合条件A的元素”相当于“存在某个符合条件A的元素”,所以直接用some
来实现。相对的还有否定形式的notAny
:
public static <T> boolean notAny(Iterable<T> iterable, Predicate<? super T> predicate) {
return !any(iterable, predicate);
}
every
的实现是:
public static <T> boolean every(Iterable<T> iterable, Predicate<? super T> predicate) {
return !some(iterable, t -> !predicate.test(t)).isPresent();
}
因为“所有元素都满足条件A”相当于“不符合条件A的元素不存在”,所以也可以用some
来实现,相对应的否定形式为notEvery
:
public static <T> boolean notEvery(Iterable<T> iterable, Predicate<? super T> predicate) {
return !every(iterable, predicate);
}