在Optional上使用get()是不好的做法吗?

时间:2016-04-27 18:18:13

标签: nullreferenceexception java-stream optional

我想总结前千个素数。当我试着这个......

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

IntelliJ建议我检查isPresent(),但这是否可能?

另一种选择是使用.orElse(-1),但我不想返回任何东西。我应该抛出异常吗?

3 个答案:

答案 0 :(得分:1)

不,在没有.get()的情况下使用.ifPresent并不一定是不好的做法。它归结为您正在实施的逻辑。如果空Optional是一种例外情况,那么依靠.get()投掷NoSuchElementException是完全合适的。

因此,您应该问自己的问题是,如果getFirstThousandPrimes()返回空列表,您的代码到底应该做什么。

答案 1 :(得分:1)

在您的特定测试中,空输入完全有效:零数之和为零。因此,您可以使用.reduce(Integer::sum).orElse(0)或完全删除.reduce(0, Integer::sum)等选项。

另请注意,您可以转换为原始流并直接使用sum()方法:

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

这样,如果输入为空,你当然也会得到0。

答案 2 :(得分:0)

通过将stream减少为参数,流可能会为空,并引入Optional来处理这种情况。还有一个reduce()方法接受identity参数,从而减少返回的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);

第二种方法比第一种方法更简洁有效。

希望这能回答你的问题。