在单个流上组合allMatch,noneMatch和anyMatch

时间:2016-12-08 09:57:51

标签: java java-8 java-stream

我想有以下逻辑:(我知道它不起作用,因为它不止一次地消耗了流)。但我不确定如何实现它。

Stream<ByteBuffer> buffers = super.getBuffers().stream();
if (buffers.allMatch(b -> b.position() > 0)) {
    return OutgoingMessageStatus.FULLY_SENT;
} else if (buffers.noneMatch(b -> b.position() > 0)) {
    return OutgoingMessageStatus.WAS_NOT_SENT;
} else {
    return OutgoingMessageStatus.PARTIALLY_SENT;
}

我该怎么做?

4 个答案:

答案 0 :(得分:8)

由于super.getBuffers()的结果为List<ByteBuffer>,您可以迭代两次。

List<ByteBuffer> buffers = super.getBuffers();
if (buffers.stream().allMatch(b -> b.position() > 0)) {
    return OutgoingMessageStatus.FULLY_SENT;
} else if (buffers.stream().noneMatch(b -> b.position() > 0)) {
    return OutgoingMessageStatus.WAS_NOT_SENT;
} else {
    return OutgoingMessageStatus.PARTIALLY_SENT;
}

请注意,在所有情况下,仍然不需要迭代所有元素。 allMatch遇到不匹配的元素时会立即返回,noneMatch会在遇到匹配元素时立即返回。因此,在PARTIALLY_SENT案例中,它可能在不查看所有元素的情况下得出结论。

另一种选择是

List<ByteBuffer> buffers = super.getBuffers();
if(buffers.isEmpty()) return OutgoingMessageStatus.FULLY_SENT;
Predicate<ByteBuffer> p = b -> b.position() > 0;
boolean sent = p.test(buffers.get(0));
if(!sent) p = p.negate();
return buffers.stream().skip(1).allMatch(p)? sent?
    OutgoingMessageStatus.FULLY_SENT:
    OutgoingMessageStatus.WAS_NOT_SENT:
    OutgoingMessageStatus.PARTIALLY_SENT;
}

第一个元素的状态决定了我们必须检查的条件。只要存在矛盾元素,allMatch立即返回,我们就会出现PARTIALLY_SENT情况。否则,所有元素都与第一个元素匹配,这意味着“全部已发送”或“未发送”。

对空列表进行预检查会产生与原始代码相同的行为,并确保get(0)永不中断。

如果您确实拥有Stream而不是可以多次迭代的源,那么就没有简单的快捷解决方案,因为这需要有状态谓词。但是,有一些处理所有元素的简单解决方案。

Map<Boolean,Long> result=getBuffers().stream()
    .collect(Collectors.partitioningBy(b -> b.position() > 0, Collectors.counting()));
return
    result.getOrDefault(false, 0L)==0?
        OutgoingMessageStatus.FULLY_SENT:
    result.getOrDefault(true, 0L)==0?
        OutgoingMessageStatus.WAS_NOT_SENT:
        OutgoingMessageStatus.PARTIALLY_SENT;

return super.getBuffers().stream()
    .map(b -> b.position() > 0?
              OutgoingMessageStatus.FULLY_SENT: OutgoingMessageStatus.WAS_NOT_SENT)
    .reduce((a,b) -> a==b? a: OutgoingMessageStatus.PARTIALLY_SENT)
    .orElse(OutgoingMessageStatus.FULLY_SENT);

答案 1 :(得分:4)

您可以使用~/.ssh/alice.pub,然后计算传递它的元素数量:

filter()

这假设Stream<ByteBuffer> buffers = super.getBuffers().stream(); int matches = buffers.filter(b -> b.position() > 0).count(); if (matches == super.getBuffers().size()) { return OutgoingMessageStatus.FULLY_SENT; } else if (matches == 0) { return OutgoingMessageStatus.WAS_NOT_SENT; } else { return OutgoingMessageStatus.PARTIALLY_SENT; } Stream)的数据源具有super.getBuffers()方法。如果没有,你可以使用一个额外的变量计算size()的总数(不太优雅,我知道):

ByteBuffer

这种方法的缺点是,当只有一些元素通过过滤器时(即输出应为int[] total = {0}; int matches = buffers.filter(b -> {total[0]++; return b.position() > 0;}).count(); if (matches == total[0]) { return OutgoingMessageStatus.FULLY_SENT; } else if (matches == 0) { return OutgoingMessageStatus.WAS_NOT_SENT; } else { return OutgoingMessageStatus.PARTIALLY_SENT; } ),它不会快速失败。也许您可以使用一些OutgoingMessageStatus.PARTIALLY_SENT操作返回三个可能输出中的一个,并且只根据需要处理多个元素。

答案 2 :(得分:1)

您需要一个集合才能重复使用它。

您可以使用count()代替collect(toList()),具体取决于您是否需要结果。

List<ByteBuffer> list = super.getBuffers();
List<ByteBuffer> buffers = list.stream().filter(b -> b.position() > 0).collect(toList());
if (buffers.size() == list.size()) {
    return OutgoingMessageStatus.FULLY_SENT;
} else if (buffers.isEmpty()) {
    return OutgoingMessageStatus.WAS_NOT_SENT;
} else {
    return OutgoingMessageStatus.PARTIALLY_SENT;
}

答案 3 :(得分:1)

我只是想用不同的类型和谓词来解决您遇到的相同问题。找不到满意的答案,所以我这样实现了自己:

Enum不仅要容纳truefalse来满足谓词,还要容纳NONE(用于空集合)和ENTANGLED(表示集合中的某些元素满足谓词,而某些不满足):

public enum QuantumBoolean {
    NONE, TRUE, FALSE, ENTANGLED;

    public static QuantumBoolean fromBoolean(boolean bool) {
        if (bool) return QuantumBoolean.TRUE;
        return QuantumBoolean.FALSE;
    }
}

仅当我需要使用特定参数调用.reduce()时,才能利用Java Stream API并遍历集合,所以我将它们打包到新类中:

public class QuantumBooleanReducer<T> {

    private final Predicate<T> predicate;
    public static final QuantumBoolean IDENTITY = QuantumBoolean.NONE;

    public QuantumBooleanReducer(Predicate<T> predicate) {
        this.predicate = predicate;
    }

    public final QuantumBoolean accumulator(QuantumBoolean state, T element) {
        QuantumBoolean newState = QuantumBoolean.fromBoolean(predicate.test(element));
        if (QuantumBoolean.NONE.equals(state))
            return newState;
        if (newState.equals(state))
            return state;
        return QuantumBoolean.ENTANGLED;
    }

    public static QuantumBoolean combiner(QuantumBoolean state1, QuantumBoolean state2) {
        if (state1.equals(state2))
            return state1;
        if (QuantumBoolean.NONE.equals(state1))
            return state2;
        if (QuantumBoolean.NONE.equals(state2))
            return state1;
        return QuantumBoolean.ENTANGLED;
    }
}

然后您可以通过调用类似的方法来解决您的问题:

switch(buffers.stream()
              .reduce(QuantumBooleanReducer.IDENTITY,
                      new QuantumBooleanReducer<ByteBuffer>(buffer -> buffer.position() > 0)::accumulator,
                      QuantumBooleanReducer::combiner)) {
            case FALSE:
                return OutgoingMessageStatus.WAS_NOT_SENT;
            case TRUE:
                return OutgoingMessageStatus.FULLY_SENT;
            case ENTANGLED:
                return OutgoingMessageStatus.PARTIALLY_SENT;
            default:
                throw new IllegalStateException("Unexpected Quantum Boolean");
}