Как «сжать» поток Java 8 от множества записей до меньшего - PullRequest
1 голос
/ 05 июня 2019

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

Ответы [ 2 ]

1 голос
/ 05 июня 2019

Рассмотрите возможность использования flatMap () , которая создаст пустой поток для начала пары и поток с одним входом для конца пары.

0 голосов
/ 05 июня 2019

Целью строки является независимое изучение элементов в потоке для других, не заботясь об обработке элемента по порядку.

В этом сценарии ваш запрос немного растянут, потому что нам нужно отслеживать предыдущий элемент «START». Более правильный способ, который я вижу, это использовать Custom Collector.

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<>();
    }
}

...