【问题标题】:Converting to functional Java style转换为函数式 Java 风格
【发布时间】:2018-03-13 00:42:25
【问题描述】:

如何使用过滤器、收集器等以适当的 Java 8 函数样式重写以下内容:

private BigInteger calculateProduct(char[] letters) {

    int OFFSET = 65;

    BigInteger[] bigPrimes = Arrays.stream(
            new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31,
            37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89,
            97, 101, 103, 107, 109, 113 })
            .mapToObj(BigInteger::valueOf)
            .toArray(BigInteger[]::new);


    BigInteger result = BigInteger.ONE;
    for (char c : letters) {
        //System.out.println(c+"="+(int)c);
        if (c < OFFSET) {
            return new BigInteger("-1");
        }
        int pos = c - OFFSET;
        result = result.multiply(bigPrimes[pos]);
    }
    return result;
}


@Test public void test() {
    assertThat(calculateProduct(capitalize("carthorse"))).isEqualTo(calculateProduct(capitalize("orchestra")));

}


private char[] capitalize(String word) {
    return word.toUpperCase().toCharArray();
}

【问题讨论】:

  • 你想要达到什么目的?
  • 我想用流利的 Java-8 风格重写 calculateProduct() 方法,用更多功能的累加器、收集器等替换循环+赋值。

标签: functional-programming java-8 refactoring functional-java


【解决方案1】:

你可以这样做:

static final BigInteger[] PRIMES
    = IntStream.of(
        2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
        59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113)
    .mapToObj(BigInteger::valueOf)
    .toArray(BigInteger[]::new);

private BigInteger calculateProduct(char[] letters) {
    final int OFFSET = 65;
    final CharBuffer cb = CharBuffer.wrap(letters);
    if(cb.chars().anyMatch(c -> c<OFFSET))
        return BigInteger.ONE.negate();
    return cb.chars()
        .mapToObj(c -> PRIMES[c-OFFSET])
        .reduce(BigInteger.ONE, BigInteger::multiply);
}

请注意,将 PRIMES 数组的创建移出方法,以避免为每次调用再次生成它,这是一项独立于您使用循环或“函数式”操作的改进。

另外,你的代码不能处理太大的字符,所以你可以改进方法来

private BigInteger calculateProduct(char[] letters) {
    final int OFFSET = 65;
    final CharBuffer cb = CharBuffer.wrap(letters);
    if(cb.chars().mapToObj(c -> c-OFFSET).anyMatch(c -> c<0||c>PRIMES.length))
        return BigInteger.ONE.negate();
    return cb.chars()
        .mapToObj(c -> PRIMES[c-OFFSET])
        .reduce(BigInteger.ONE, BigInteger::multiply);
}

【讨论】:

  • 哦,这很干净!
  • 我猜参数数组char[] letters也可以做成final吧?
  • 是的,您也可以将letters 声明为final。但这只是一种风格选择,对代码没有任何影响(它已经实际上是最终的)。唯一会产生影响的变量是 OFFSET 变量,当它具有 final 修饰符时,它是一个编译时常量。
【解决方案2】:

我不知道你为什么要这样做,但可能是这样(它会创建更多对象并且更冗长):

private static BigInteger calculateProduct(char[] letters) {

    int OFFSET = 65;

    BigInteger[] bigPrimes = Arrays.stream(
            new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31,
                    37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89,
                    97, 101, 103, 107, 109, 113 })
            .mapToObj(BigInteger::valueOf)
            .toArray(BigInteger[]::new);

    Optional<Character> one = IntStream.range(0, letters.length)
            .mapToObj(x -> letters[x])
            .filter(x -> x < OFFSET)
            .findAny();

    if (one.isPresent()) {
        return new BigInteger("-1");
    } else {
        return IntStream.range(0, letters.length)
                .mapToObj(x -> letters[x])
                .parallel()
                .reduce(
                        BigInteger.ONE,
                        (x, y) -> {
                            int pos = y - OFFSET;
                            return x.multiply(bigPrimes[pos]);
                        },
                        BigInteger::multiply);
    }

}

【讨论】:

  • 为什么每次评估都将BigInteger.ONE 乘以结果?将某事乘以 1 不会改变结果,这也适用于 BigInteger(并且 OP 的代码不会这样做)。一般来说,……为什么这么复杂?没有必要将char 值作为Character 处理,尤其是,因为它们只进行了算术运算。
  • @Holger 你完全正确,我完全错过了
猜你喜欢
  • 2013-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-06
  • 1970-01-01
  • 2016-01-08
  • 1970-01-01
  • 2016-11-25
相关资源
最近更新 更多