Подтвердить что ты не робот

Как получить ряд элементов из потока с помощью Java 8 lambda?

В предыдущем вопросе [Как динамически выполнять фильтрацию в Java 8?] Стюарт Маркс дал прекрасный ответ и предоставил несколько полезных утилит для обработки выбора topN и topPercent from stream.

Я включу их здесь из своего первоначального ответа:

@FunctionalInterface
public interface Criterion {
    Stream<Widget> apply(Stream<Widget> s);
}

Criterion topN(Comparator<Widget> cmp, long n) {
    return stream -> stream.sorted(cmp).limit(n);
}

Criterion topPercent(Comparator<Widget> cmp, double pct) {
    return stream -> {
        List<Widget> temp =
            stream.sorted(cmp).collect(toList());
        return temp.stream()
                   .limit((long)(temp.size() * pct));
    };
}

Мои вопросы здесь:

[1] Как получить верхние элементы от 3 до 7 из потока с определенным количеством элементов, поэтому, если в потоке есть элементы из A1, A2.. A10, вызов

topNFromRange(Comparator<Widget> cmp, long from, long to) = topNFromRange(comparing(Widget::length), 3L, 7L)

вернет {A3, A4, A5, A6, A7}

Самый простой способ, по которому я могу думать, - получить верхнюю 7 [T7] от оригинала, получить верхнюю 3 [T3] от оригинала, а затем получить T7 - ​​T3.

[2] Как получить верхние позиции от 10% до 30% от потока с определенным количеством элементов, поэтому, если в потоке есть элементы из X1, X2.. X100, вызов

topPercentFromRange(Comparator<Widget> cmp, double from, double to) = topNFromRange(comparing(Widget::length), 0.10, 0.30)

вернет {X10, X11, X12,..., X29, X30}

Самый простой способ, который я могу придумать, - получить 30% лучших [TP30] от оригинала, получить топ 10% [TP10] от оригинала, а затем получить TP30 - TP10.

Каковы некоторые лучшие способы использования Java 8 Lambda для краткого выражения вышеуказанных ситуаций?

4b9b3361

Ответ 1

Пользователь skiwi уже ответил на первую часть вопроса. Вторая часть:

(2) Как получить верхние позиции от 10% до 30% от потока с определенным количеством предметов....

Чтобы сделать это, вы должны использовать аналогичный метод, как topPercent в ответе , на другой вопрос. То есть вам нужно собрать элементы в список, чтобы иметь возможность подсчитать количество элементов, возможно, после того, как была выполнена какая-либо фильтрация по потоку.

Как только у вас есть счетчик, вы вычисляете правильные значения для skip и limit на основе количества и процентов, которые вы хотите. Возможно, что-то подобное:

Criterion topPercentFromRange(Comparator<Widget> cmp, double from, double to) {
    return stream -> {
        List<Widget> temp =
            stream.sorted(cmp).collect(toList());
        return temp.stream()
                   .skip((long)(temp.size() * from))
                   .limit((long)(temp.size() * (to - from)));
    };
}

Конечно, вам нужно будет выполнить проверку ошибок на from и to. Более тонкая проблема заключается в определении количества элементов для испускания. Например, если у вас есть десять элементов, они имеют индексы [0..9], которые соответствуют 0%, 10%, 20%,..., 90%. Но если бы вы попросили диапазон от 9% до 11%, приведенный выше код не испустил бы никаких элементов, а не тот, который был бы на 10%, как и следовало ожидать. Таким образом, некоторые манипуляции с процентными вычислениями, вероятно, необходимы для соответствия семантике того, что вы пытаетесь сделать.

Ответ 2

Чтобы получить диапазон от Stream<T>, вы можете использовать skip(long n), чтобы сначала пропустить определенное количество элементов, а затем вы можете вызвать limit(long n), чтобы принимать только определенное количество элементов.

Рассмотрим поток с 10 элементами, затем для получения элементов с 3 по 7 вы обычно вызываете из List:

list.subList(3, 7);

Теперь с Stream вам нужно сначала пропустить 3 элемента, а затем взять 7 - 3 = 4 элемента, поэтому он станет следующим:

stream.skip(3).limit(4);

Как вариант решения @StuartMarks для второго ответа, я предлагаю вам следующее решение, которое оставляет возможность цепочки целыми, оно работает так же, как это делает @StuartMarks:

private <T> Collector<T, ?, Stream<T>> topPercentFromRangeCollector(Comparator<T> comparator, double from, double to) {
    return Collectors.collectingAndThen(
        Collectors.toList(),
        list -> list.stream()
            .sorted(comparator)
            .skip((long)(list.size() * from))
            .limit((long)(list.size() * (to - from)))
    );
}

и

IntStream.range(0, 100)
        .boxed()
        .collect(topPercentFromRangeCollector(Comparator.comparingInt(i -> i), 0.1d, 0.3d))
        .forEach(System.out::println);

Это напечатает элементы с 10 по 29.

Он работает с помощью Collector<T, ?, Stream<T>>, который принимает ваши элементы из потока, преобразует их в List<T>, затем получает Stream<T>, сортирует его и применяет к нему (правильные) границы.