代码之家  ›  专栏  ›  技术社区  ›  AdHominem

在Optional上使用get()是不是不好?

  •  0
  • AdHominem  · 技术社区  · 8 年前

    我想对前1000个质数求和。当我尝试这个时。。。

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

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

    另一个选项是使用.orElse(-1),但我不想返回任何内容。我应该抛出异常吗?

    3 回复  |  直到 8 年前
        1
  •  1
  •   Tagir Valeev    8 年前

    在您的特定测试中,输入为空是完全有效的:零数字之和为零。所以你可以使用 .reduce(Integer::sum).orElse(0) 或者完全摆脱诸如 .reduce(0, Integer::sum) .

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

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

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

        2
  •  1
  •   Misha    8 年前

    不,使用它不一定是一种坏习惯 .get() 没有 .ifPresent 归根结底,这取决于您正在实现的逻辑。如果为空 Optional 是一个例外情况,依靠它是完全合适的 .get() 抛出一个 NoSuchElementException .

    因此,您应该问自己,如果 getFirstThousandPrimes() 返回空列表。

        3
  •  0
  •   dawid gdanski    8 年前

    通过将流简化为参数,流可能为空 Optional 介绍来处理这个案例。还有一个reduce()方法,它接受标识参数,因此reduce 可选择的 返回,因为在空流的情况下,将返回标识。

    但针对您的任务,我建议使用自定义收集器来划分质数和非质数,然后对前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);
    

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

    希望这能回答你的问题。