从Java8流中删除与模式匹配的序列
我发现了Java8,尤其是流的使用,它似乎非常强大。然而,我在表达查询时遇到了一个问题 我有一个要分析的事件对象列表。我想确定此列表中一些不好的模式(事件序列),应该删除。 基本上,事件对象有3个字段:从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”) 如果我有两个事件与相同的源和相同的度量有关,但它
- Element eventSource(元素超类的对象,例如VirtualMachine)
- 字符串eventName(事件类的类型,例如“VMHighCpu”或“VMLowCpu”)
- 字符串eventMetric(涉及的度量,例如“cpu”)
// 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;
}
正常,高,低,高,正常→ 正常,正常
如果有一系列的事件并不都是对立的,有些会被移除,但对立的部分可能会保留在流程中:
正常,高,高,低,低,正常→ 正常,高,低,正常
这取决于您要删除的模式的确切规格,但我相信,通过调整布尔数组的处理,您可以完成大多数您想做的事情。尝试在赋值的右侧消除对原始贴图的强制转换。