Объединение нескольких потоков Java в структурированном виде

Я хочу использовать потоковый API Java для выполнения некоторых вычислений в списке объектов:

List<Item>.stream()...

Класс Item содержит множество атрибутов. Для некоторых из них мне нужно взять среднее значение по всем элементам в коллекции, для других атрибутов мне нужно выполнить другие формы вычислений. Для этого я выполнял отдельные вызовы потока/коллектора, и хотя я не сталкиваюсь с какими-либо проблемами с производительностью (поскольку размер списка обычно составляет около 100), я хочу научиться быть более кратким, например, цикл один раз.

ItemCalculation itemCalculation = ItemCalculation.builder()
    .amountOfItems(itemList.size())
    .averagePrice(itemList.stream()
            .mapToDouble(item -> item.getPrice())
            .average()
            .getAsDouble())
    .averageInvestmentValue(itemList.stream()
            .mapToDouble(item -> getTotalInvestmentValue(item.getInvestmentValue(), item.getInvestmentValuePackaging()))
            .average()
            .getAsDouble())
    .highestWarrantyLimit(itemList.stream()... etc.

Я читал о создании собственного коллектора, но мне кажется немного странным, что мой класс «вычисления» состоит всего из одной строки (stream->customCollector), а затем имеет очень раздутый класс коллектора, который выполняет реальную логику. Тем более, что разные атрибуты собираются по-разному, мне понадобится много разных промежуточных счетчиков и других переменных. Какие-нибудь мысли?


person user1884155    schedule 24.04.2018    source источник
comment
Я бы сделал собственный сборщик, который в результате создает ItemCalculation.   -  person Johannes Kuhn    schedule 24.04.2018
comment
хотя это возможно, обычный цикл for будет более читабельным   -  person Bogdan Lukiyanchuk    schedule 24.04.2018
comment
также посмотрите stackoverflow. ком/вопросы/44942493/   -  person Bogdan Lukiyanchuk    schedule 24.04.2018
comment
вы можете взять этот ответ stackoverflow.com/a/44959821/1059372 и использовать его в качестве служебного метода в своем проекте, таким образом скрыть всю сложность   -  person Eugene    schedule 24.04.2018
comment
В зависимости от размера вашего itemList у вас могут возникнуть проблемы с производительностью при работе с потоками, если размер меньше нескольких сотен. Это будет зависеть от операции и, возможно, от самого объекта, поэтому лучше, если вы сможете запустить тест производительности. Помимо производительности, приведенный выше код может быть трудно поддерживать из-за удобочитаемости.   -  person alltej    schedule 07.05.2018
comment
Это похоже на вопрос, на который я ответил здесь: stackoverflow.com/a/56929151/6473398 ) пользовательский сборщик, б) пользовательский аккумулятор и reduce или в) Collectors.teeing.   -  person sfiss    schedule 10.07.2019


Ответы (1)


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

Код, который вы указали в своем вопросе, ясен для понимания и достаточно эффективен для небольшой коллекции, как сейчас.

Если вы хотите повысить производительность своего решения, вы можете выполнить итерацию по своей коллекции только один раз в итеративном режиме, вычисляя все необходимые выходные данные за один запуск:

    long amountOfItems = 0;
    double priseSum = 0;
    double highestWarrantyLimit = Double.MIN_VALUE;
    for (Item item : itemList) {
        amountOfItems++;
        priseSum += item.getPrice();
        double investmentValue = getTotalInvestmentValue(item.getInvestmentValue(), item.getInvestmentValuePackaging());
        if (highestWarrantyLimit < investmentValue) {
            highestWarrantyLimit = investmentValue;
        }
    }
    ItemCalculation itemCalculation = ItemCalculation.builder()
            .amountOfItems(amountOfItems)
            .averagePrice(priseSum / amountOfItems)
            .averageInvestmentValue(investmentValueSum / amountOfItems)
            .highestWarrantyLimit(highestWarrantyLimit)
            // ...
            .build(); 

API потоков был добавлен для обеспечения поддержки библиотек для обработки последовательностей элементов данных, что очень актуально для вашего случая. Однако потоки накладывают общий конвейер для элементов данных, что неверно для вашего случая и делает конвейер похожим на:

itemList.stream()
    .collect(toItemCalculation());

Что не очень разумно, если только вы не собираетесь работать в многопоточном режиме. В таком случае предпочтительнее будет решение, использующее настраиваемый сборщик, так как скаффолдинг-код для объединения логики уже встроен.

person Pavel    schedule 14.09.2018