当前位置:  开发笔记 > 编程语言 > 正文

在一个流中进行多个"匹配"检查

如何解决《在一个流中进行多个"匹配"检查》经验,为你挑选了3个好方法。

是否可以检查数组(或集合)是否包含元素5 5 以外的元素.在一个流中返回布尔结果而不是使用两个流:

int[] ints = new int[]{1, 2, 3, 4, 5};

boolean hasFive = IntStream.of(ints).anyMatch(num -> num == 5);
boolean hasNonFive = IntStream.of(ints).anyMatch(num -> num != 5);

boolean result = hasFive && hasNonFive;

Holger.. 6

在这种特定情况下,即您想知道流或数组是否包含匹配和非匹配元素(与谓词的否定匹配的元素),您可以更简单地做到这一点.

首先,测试第一个元素是否与谓词或其否定匹配,然后,搜索流是否包含相反的任何匹配:

IntPredicate predicate=i -> i==5;

if(ints.length>0 && predicate.test(ints[0]))
    predicate=predicate.negate();
boolean result = IntStream.of(ints).anyMatch(predicate);

而已.如果您没有数组或集合作为流源,而是任意流,则测试第一个元素有点棘手:

IntPredicate[] tmp={ null };
Spliterator.OfInt sp=intStream.spliterator();
boolean result = sp.tryAdvance(
    (int i) -> tmp[0]=predicate.test(i)? predicate.negate(): predicate)
 && StreamSupport.intStream(sp, false).anyMatch(tmp[0]);


Tagir Valeev.. 6

这是涉及我的StreamEx库的两个解决方案.我在这里使用的核心功能是短路收集器的概念.我的库增强了Collector概念,提供短路能力(适用于顺序和并行流)

如果谓词与样本中的一样(一个与另一个相反),您可以使用partitioningBy:

Map> map = IntStreamEx.of(ints).boxed()
        .partitioningBy(num -> num == 5, MoreCollectors.first());

现在您应该检查两个映射是否存在:

System.out.println(map.values().stream().allMatch(Optional::isPresent));

或者在单一声明中:

System.out.println(IntStreamEx.of(ints).boxed()
        .partitioningBy(num -> num == 5, MoreCollectors.first())
        .values().stream().allMatch(Optional::isPresent));

我们在这里使用MoreCollectors.first()短路收集器.此解决方案类似于@ user140547提出的解决方案,但实际上只要找到两个元素就会停止处理.


对于两个自定义谓词,可以使用pairing收集器,它结合了两个收集器的结果(如果输入收集器是短路的,则保留短路).但首先,我们需要anyMatching收藏家(我的图书馆中没有):

import static one.util.streamex.MoreCollectors.*;

static  Collector anyMatching(Predicate pred) {
    return collectingAndThen(filtering(pred, first()), Optional::isPresent);
}

Collector hasFive = anyMatching(num -> num == 5); 
Collector hasNonFive =  anyMatching(num -> num != 5);
Collector hasBoth = pairing(hasFive, hasNonFive, 
          (res1, res2) -> res1 && res2);

System.out.println(IntStreamEx.of(ints).boxed().collect(hasBoth));


Tunaki.. 5

我认为如何做到这一点的一种方法是IntPredicate从多个IntPredicates 创建一个自定义.每次测试一个值时,我们都会尝试从这个匹配它的数组中找到一个谓词,如果匹配,我们将它存储在一个内部Set(正确处理重复).当存储的集与初始数组具有相同的大小时,表示所有谓词都已匹配,并且我们的自定义谓词可以返回true.

我的初始解决方案使用a Set来存储匹配的谓词的索引.正如@Holger评论的那样,使用a BitSet并存储不匹配谓词的索引可能更有效.

private static class MultipleIntPredicate implements IntPredicate {

    private IntPredicate[] predicates;
    private BitSet unmatchedPredicates;

    public MultipleIntPredicate(IntPredicate... predicates) {
        this.predicates = predicates;
        unmatchedPredicates = new BitSet(predicates.length);
        unmatchedPredicates.set(0, predicates.length, true); // initially, all predicates are unmatched
    }

    @Override
    public boolean test(int value) {
        unmatchedPredicates.stream()
                           .filter(i -> predicates[i].test(value))
                           .findFirst()
                           .ifPresent(unmatchedPredicates::clear); // when a match is found, clear the BitSet
        return unmatchedPredicates.isEmpty(); // return true if all the predicates were matched
    }

}

像这样使用它:

int[] ints = new int[] {1, 2, 3, 4, 5};
MultipleIntPredicate predicate = new MultipleIntPredicate(num -> num == 5, num -> num != 5);
boolean hasFiveAndNonFive = IntStream.of(ints).anyMatch(predicate);
System.out.println(hasFiveAndNonFive);

对于数组的情况,就像你的问题一样,这个解决方案可能比在数组上迭代两次更有开销.但是,在无限的情况下IntStream,此谓词仍然可以正常工作.它还具有以下优点:所需谓词不必与其自身相反.



1> Holger..:

在这种特定情况下,即您想知道流或数组是否包含匹配和非匹配元素(与谓词的否定匹配的元素),您可以更简单地做到这一点.

首先,测试第一个元素是否与谓词或其否定匹配,然后,搜索流是否包含相反的任何匹配:

IntPredicate predicate=i -> i==5;

if(ints.length>0 && predicate.test(ints[0]))
    predicate=predicate.negate();
boolean result = IntStream.of(ints).anyMatch(predicate);

而已.如果您没有数组或集合作为流源,而是任意流,则测试第一个元素有点棘手:

IntPredicate[] tmp={ null };
Spliterator.OfInt sp=intStream.spliterator();
boolean result = sp.tryAdvance(
    (int i) -> tmp[0]=predicate.test(i)? predicate.negate(): predicate)
 && StreamSupport.intStream(sp, false).anyMatch(tmp[0]);



2> Tagir Valeev..:

这是涉及我的StreamEx库的两个解决方案.我在这里使用的核心功能是短路收集器的概念.我的库增强了Collector概念,提供短路能力(适用于顺序和并行流)

如果谓词与样本中的一样(一个与另一个相反),您可以使用partitioningBy:

Map> map = IntStreamEx.of(ints).boxed()
        .partitioningBy(num -> num == 5, MoreCollectors.first());

现在您应该检查两个映射是否存在:

System.out.println(map.values().stream().allMatch(Optional::isPresent));

或者在单一声明中:

System.out.println(IntStreamEx.of(ints).boxed()
        .partitioningBy(num -> num == 5, MoreCollectors.first())
        .values().stream().allMatch(Optional::isPresent));

我们在这里使用MoreCollectors.first()短路收集器.此解决方案类似于@ user140547提出的解决方案,但实际上只要找到两个元素就会停止处理.


对于两个自定义谓词,可以使用pairing收集器,它结合了两个收集器的结果(如果输入收集器是短路的,则保留短路).但首先,我们需要anyMatching收藏家(我的图书馆中没有):

import static one.util.streamex.MoreCollectors.*;

static  Collector anyMatching(Predicate pred) {
    return collectingAndThen(filtering(pred, first()), Optional::isPresent);
}

Collector hasFive = anyMatching(num -> num == 5); 
Collector hasNonFive =  anyMatching(num -> num != 5);
Collector hasBoth = pairing(hasFive, hasNonFive, 
          (res1, res2) -> res1 && res2);

System.out.println(IntStreamEx.of(ints).boxed().collect(hasBoth));



3> Tunaki..:

我认为如何做到这一点的一种方法是IntPredicate从多个IntPredicates 创建一个自定义.每次测试一个值时,我们都会尝试从这个匹配它的数组中找到一个谓词,如果匹配,我们将它存储在一个内部Set(正确处理重复).当存储的集与初始数组具有相同的大小时,表示所有谓词都已匹配,并且我们的自定义谓词可以返回true.

我的初始解决方案使用a Set来存储匹配的谓词的索引.正如@Holger评论的那样,使用a BitSet并存储不匹配谓词的索引可能更有效.

private static class MultipleIntPredicate implements IntPredicate {

    private IntPredicate[] predicates;
    private BitSet unmatchedPredicates;

    public MultipleIntPredicate(IntPredicate... predicates) {
        this.predicates = predicates;
        unmatchedPredicates = new BitSet(predicates.length);
        unmatchedPredicates.set(0, predicates.length, true); // initially, all predicates are unmatched
    }

    @Override
    public boolean test(int value) {
        unmatchedPredicates.stream()
                           .filter(i -> predicates[i].test(value))
                           .findFirst()
                           .ifPresent(unmatchedPredicates::clear); // when a match is found, clear the BitSet
        return unmatchedPredicates.isEmpty(); // return true if all the predicates were matched
    }

}

像这样使用它:

int[] ints = new int[] {1, 2, 3, 4, 5};
MultipleIntPredicate predicate = new MultipleIntPredicate(num -> num == 5, num -> num != 5);
boolean hasFiveAndNonFive = IntStream.of(ints).anyMatch(predicate);
System.out.println(hasFiveAndNonFive);

对于数组的情况,就像你的问题一样,这个解决方案可能比在数组上迭代两次更有开销.但是,在无限的情况下IntStream,此谓词仍然可以正常工作.它还具有以下优点:所需谓词不必与其自身相反.


那么这个解决方案使用了一个可变的"BitSet",为什么不能避免这种情况,因为整个Stream API都是关于避免可变状态的?
推荐阅读
echo7111436
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有