从Java8流中删除与模式匹配的序列

从Java8流中删除与模式匹配的序列,java,java-8,grouping,java-stream,collectors,Java,Java 8,Grouping,Java Stream,Collectors,我发现了Java8,尤其是流的使用,它似乎非常强大。然而,我在表达查询时遇到了一个问题 我有一个要分析的事件对象列表。我想确定此列表中一些不好的模式(事件序列),应该删除。 基本上,事件对象有3个字段: Element eventSource(元素超类的对象,例如VirtualMachine) 字符串eventName(事件类的类型,例如“VMHighCpu”或“VMLowCpu”) 字符串eventMetric(涉及的度量,例如“cpu”) 如果我有两个事件与相同的源和相同的度量有关,但它

我发现了Java8,尤其是流的使用,它似乎非常强大。然而,我在表达查询时遇到了一个问题

我有一个要分析的事件对象列表。我想确定此列表中一些不好的模式(事件序列),应该删除。

基本上,事件对象有3个字段:

  • Element eventSource(元素超类的对象,例如VirtualMachine)
  • 字符串eventName(事件类的类型,例如“VMHighCpu”或“VMLowCpu”)
  • 字符串eventMetric(涉及的度量,例如“cpu”)
如果我有两个事件与相同的源和相同的度量有关,但它们是相反的(例如,一个是“VMHighCpu”类型,另一个是“VMLowCpu”类型),我想从列表中删除这两个事件

我尝试了几件事都没有成功

    // Simple query
    Map<Element, List<EventToAnalyze>> bySource = (Map) eventsToPurge.stream().collect(Collectors.groupingBy(EventToAnalyze::getSource));

    // Another attempt
    Map<Element, List<EventToAnalyze>> bySourceWithFilter = (Map) eventsToPurge.stream().filter(e -> e.getEventName().contains("Low")).collect(Collectors.groupingBy(EventToAnalyze::getSource));

    // Last attempt
    Map<Element, List<EventToAnalyze>> bySourceByMetric = (Map) eventsToPurge.stream().collect(Collectors.groupingBy(
                                    EventToAnalyze::getSource, Collectors.groupingBy(
                                                    EventToAnalyze::getMetricName, Collectors.groupingBy(
                                                                    EventToAnalyze::getEventName))));
//简单查询
Map bySource=(Map)EventStopPurge.stream().collect(Collectors.groupingBy(EventToAnalyze::getSource));
//又一次尝试
Map bySourceWithFilter=(Map)eventstopuke.stream().filter(e->e.getEventName().contains(“Low”).collect(Collectors.groupingBy(EventToAnalyze::getSource));
//最后一次尝试
Map bySourceByMetric=(Map)EventStopPurge.stream().collect(Collectors.groupingBy)(
EventToAnalyze::getSource,Collectors.groupingBy(
EventToAnalyze::getMetricName,Collectors.groupingBy(
EventToAnalyze::getEventName));

希望在我的解释中已经清楚了。

使用流做什么有些困难,因为大多数流操作都是在流中的每个值上进行的,独立于其他值。有一些有状态的操作,如
distinct
sort
,但这些操作有些不寻常,无法自定义

您可以编写自己的有状态操作(类似于)。在这种情况下,它将是一个有状态的flatmapper,但我不清楚如何让它工作

这里有一种基于数组的替代方法。它假定您可以随机访问事件。你说你有一个事件列表,所以我希望是这样。为了简单起见,让我们这样设置:

enum Event {
    HIGH, NORMAL, LOW
}
我们需要一个函数来获取两个事件,并确定它们是否匹配要删除的模式:

boolean match(Event e1, Event e2) {
    return e1 == Event.HIGH && e2 == Event.LOW
        || e1 == Event.LOW && e2 == Event.HIGH;
}
请注意,这与
BiPredicate
功能接口相匹配

作为设置的最后一部分,让我们介绍一个助手,它在给定子范围内对数组的每个索引调用lambda函数。这与Arrays.setAll类似,只是它接受一个子范围,并对布尔[]进行操作

void ArraySetRange(boolean[] array, int start, int end, IntPredicate op) {
    IntStream.range(start, end).forEach(i -> array[i] = op.test(i));
}
现在安装完成了。主要任务是什么?给定一个事件列表,我们希望删除与某些模式匹配的事件序列,并返回一个事件列表:

List<Event> remove(List<Event> input, BiPredicate<Event,Event> matcher) {
    ...
这会将布尔值
true
放在数组中此事件与其左侧的事件匹配模式的每个位置。请注意,模式可以重叠。我们跳过数组的第一个元素,因为它的左边什么都没有

但我们想删除整个模式。对于每个事件,如果其右侧的元素是模式的右端,那么我们也要删除该元素。这是另一个数组操作,但这一次对所有数组索引执行,最后一个除外:

    ArraySetRange(flags, 0, n-1, i -> flags[i] || flags[i+1]);
(请注意,这会根据输入数组中的值修改输入数组。如果处理是从左到右顺序进行的,这会起作用,但如果我们希望并行执行,最好将结果存储到单独的数组中。)

现在我们有了一个数组标志,其中
true
表示我们要删除的模式中存在。我们可以使用简单的筛选操作来完成此操作:

    return IntStream.range(0, n)
        .filter(i -> ! flags[i])
        .mapToObj(input::get)
        .collect(toList());
完整的示例如下:

List<Event> remove(List<Event> input, BiPredicate<Event,Event> matcher) {
    int n = input.size();
    boolean[] flags = new boolean[n];
    ArraySetRange(flags, 1, n, i -> matcher.test(input.get(i-1), input.get(i)));
    ArraySetRange(flags, 0, n-1, i -> flags[i] || flags[i+1]);
    return IntStream.range(0, n)
        .filter(i -> ! flags[i])
        .mapToObj(input::get)
        .collect(toList());
}
列表删除(列表输入,双预测匹配器){
int n=input.size();
布尔[]标志=新布尔[n];
ArraySetRange(flags,1,n,i->matcher.test(input.get(i-1),input.get(i));
ArraySetRange(flags,0,n-1,i->flags[i]| | flags[i+1]);
返回IntStream.range(0,n)
.filter(i->!标志[i])
.mapToObj(输入::get)
.collect(toList());
}
你可以这样称呼它:

    List<Event> purgedEvents = remove(eventsToPurge, this::match);
List purgedEvents=remove(eventstopunce,this::match);
我暗自怀疑这不是你想要的。这将很好地删除隔离对:

正常,高,低,正常→ 正常,正常

但如果连续发生三个“相反”的事件,它们都将被删除:

boolean match(Event e1, Event e2) {
    return e1 == Event.HIGH && e2 == Event.LOW
        || e1 == Event.LOW && e2 == Event.HIGH;
}
正常,高,低,高,正常→ 正常,正常

如果有一系列的事件并不都是对立的,有些会被移除,但对立的部分可能会保留在流程中:

正常,高,高,低,低,正常→ 正常,高,低,正常


这取决于您要删除的模式的确切规格,但我相信,通过调整布尔数组的处理,您可以完成大多数您想做的事情。

尝试在赋值的右侧消除对原始贴图的强制转换。