Как я могу "сжать" поток 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();
}
}