如何将包含很多项目的Java 8流“缩小”到包含更少项目的流?
我不是在问映射,每个输入项有1个“输出”项,或者在流减少到单个值的情况下减少,而是将许多项的流缩小到更少的项。 “收缩”是有状态的;发射一个物品是基于1个或多个以前的物品(尽管它只是向前移动,所以状态非常简单)。
我有一堆简单的带有时间戳的事件; START或STOP事件。我需要将这种简单事件流简化为记录,每个记录都包含开始和停止时间。在最简单的情况下,有一个START和STOP对,但是重复进行START而又不介入STOP是完全合法的。尽管变质了,但重复停止也是合法的。
下面是一个演示的(简化)版本。查看input
和expected
之间的区别;输入项多于输出项。
关键是,shrinkEvents签名是基于流而不是列表。我想要一个不需要List<String> output
中的中间shrinkEvents
的版本。
public class ShrinkStream {
@Test
public void shrinkStream() {
Stream<String> input = Stream.of("START@1", "STOP@12", "START@14", "START@24", "STOP@35", "STOP@45");
List<String> expected = Arrays.asList("1-12", "14-24", "24-35");
Stream<String> actual = shrinkEvents(input);
assertEquals(expected, actual.collect(toList()));
}
private Stream<String> shrinkEvents(Stream<String> input) {
List<String> output = new ArrayList<>();
final StringBuilder startTime = new StringBuilder(); // mutable (effectively final BS)
input.forEach(s -> {
String[] tokens = s.split("@");
String type = tokens[0];
String time = tokens[1];
boolean isAlreadyActive = startTime.length() > 0;
if (isAlreadyActive)
output.add(startTime + "-" + time);
startTime.setLength(0); // reset
if (type.equals("START"))
startTime.append(time);
});
return output.stream();
}
}
答案 0 :(得分:1)
请考虑使用flatMap(),它将在该对的开头生成空流,而在该对的结尾生成单项流。
答案 1 :(得分:0)
字符串的目的是独立于其他对象检查Stream内部的元素,而不必担心按顺序处理元素。
在这种情况下,您的要求有些紧张,因为我们需要跟踪上一个“ START”元素。 我看到的更正确的方法是使用自定义收集器。
public class ShrinkStream {
@Test
public void shrinkStream() {
Stream<String> input = Stream.of("START@1", "STOP@12", "START@14", "START@24", "STOP@35", "STOP@45").parallel();
List<String> expected = Arrays.asList("1-12", "14-24", "24-35");
MyShrinkCollector myShrinkCollector= new MyShrinkCollector();
assertEquals(expected, input.collect(myShrinkCollector));
}
}
public class MyShrinkCollector implements Collector<String, List<String>, List<String>> {
private String startNumber = null;
@Override
public Supplier<List<String>> supplier() {
return ArrayList::new;
}
@Override
public BiConsumer<List<String>, String> accumulator() {
return (list, val) -> {
String[] s = val.split("@");
String type = s[0];
String num = s[1];
if (startNumber != null) {
list.add(startNumber + "-" + num);
startNumber = null;
}
if (type.equals("START")) startNumber = num;
};
}
@Override
public BinaryOperator<List<String>> combiner() {
return null;
}
@Override
public Function<List<String>, List<String>> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return new HashSet<>();
}
}