Я хочу просуммировать первую тысячу простых чисел. Когда я пробую это ...

System.out.println(getFirstThousandPrimes().stream()
                                           .reduce(Integer::sum)
                                           .get()
    );

IntelliJ предлагает мне проверить isPresent (), но возможно ли это?

Другой вариант - использовать .orElse (-1), но я не хочу ничего возвращать. Должен ли я выбросить исключение?

0
AdHominem 27 Апр 2016 в 21:18

3 ответа

Лучший ответ

В вашем конкретном тесте с пустым вводом вполне допустимо: сумма нулевых чисел равна нулю. Таким образом, вы можете использовать .reduce(Integer::sum).orElse(0) или полностью отказаться от таких опций, как .reduce(0, Integer::sum).

Также обратите внимание, что вы можете преобразовать в примитивный поток и напрямую использовать метод sum():

getFoos().stream().mapToInt(x -> x).sum();

Таким образом, вы, конечно, также получите 0, если вход пуст.

1
Tagir Valeev 28 Апр 2016 в 08:10

При уменьшении потока до аргумента существует вероятность, что поток будет пустым, и для обработки этого случая вводится Optional. Есть еще один метод reduce (), который принимает аргумент идентичности и, таким образом, уменьшает возвращаемый Optional, потому что в случае пустого потока идентичность будет возвращена.

Но, сосредоточившись на вашей задаче, я бы рекомендовал использовать специальный сборщик для разделения простых и непростых чисел, а затем суммировать первые n чисел.

Некоторое время назад я реализовал сборщик простых и непростых чисел, и реализация выглядит следующим образом:

public class PrimeNumberCollector implements Collector<Integer,
    Map<Boolean, List<Integer>>,
    Map<Boolean, List<Integer>>> {

@Override
public Supplier<Map<Boolean, List<Integer>>> supplier() {
    return () -> new HashMap<Boolean, List<Integer>>() {{
        put(Boolean.TRUE, new ArrayList<>());
        put(Boolean.FALSE, new ArrayList<>());
    }};
}

@Override
public BiConsumer<Map<Boolean, List<Integer>>, Integer> accumulator() {
    return (Map<Boolean, List<Integer>> acc, Integer candidate) -> acc.get(isPrime(candidate))
            .add(candidate);
}

@Override
public BinaryOperator<Map<Boolean, List<Integer>>> combiner() {
    return (Map<Boolean, List<Integer>> firstMap, Map<Boolean, List<Integer>> secondMap) -> {
        firstMap.get(Boolean.TRUE).addAll(secondMap.get(Boolean.TRUE));
        firstMap.get(Boolean.FALSE).addAll(secondMap.get(Boolean.FALSE));

        return firstMap;
    };
}

@Override
public Function<Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> finisher() {
    return Function.identity();
}

@Override
public Set<Characteristics> characteristics() {
    return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
}

private static boolean isPrime(final int candidate) {
    return IntStream.rangeClosed(2, (int) Math.sqrt(candidate)).noneMatch(i -> candidate % i == 0);
}
}

И его использование:

@Test
public void collectingPrimeNumbersWithCustomCollector() {
    Map<Boolean, List<Integer>> primesNumbersMap = IntStream.rangeClosed(1, 1_000_000)
            .boxed()
            .collect(CustomCollectors.primeNumbers()); //or new PrimeNumbersCollector()

    Utils.printLine("Prime numbers between 1 - 1_000_000:");
    Utils.printLine(primesNumbersMap.get(Boolean.TRUE));
}

Затем вы можете limit(1000) и sum(0, BinaryOperator) все простые числа до достижения предела количества.

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

 private static boolean isPrime(final int candidate) {
        return IntStream.rangeClosed(2, (int) Math.sqrt(candidate)).noneMatch(i -> candidate % i == 0);
    }

Использование будет выглядеть следующим образом:

Stream.iterate(1L, i -> i + 1) //iterate with sequential numbers
                .filter(MyFancyClass::isPrime)
                .limit(1000)
                .reduce(0L, Long::sum);

Второй подход более лаконичен и эффективен, чем первый.

Надеюсь, что это ответ на ваш вопрос.

0
dawid gdanski 27 Апр 2016 в 18:58

Нет, использование .get() без .ifPresent - не всегда плохая практика. Все сводится к логике, которую вы реализуете. Если пустой Optional является исключительным условием, совершенно уместно полагаться на .get() для выдачи NoSuchElementException.

Поэтому вопрос, который вы должны задать себе, - что именно должен делать ваш код, если getFirstThousandPrimes() возвращает пустой список.

1
Misha 27 Апр 2016 в 21:18