2

Background:

When you extract methods out of long code pieces, you often run into the call-by-value problem with primitive variables. You cannot change those primitive parameters in the extracted method so that the caller sees the changes. You can avoid that by making the primitive variable an array with only one element. Then it is effectively used call-by-reference. However it is now an object on the heap. Is the escape analysis of Java clever enough to understand that and use the stack despite that?

Given following code and the case it could not be inlined:

public class EscapeAnalysisTest {
    public static void main(String[] args) {
        final Set<Integer> integers = new HashSet<>();
        integers.add(1);
        integers.add(9);
        integers.add(8);
        integers.add(4);
        // and so on ...

        final long[] product = new long[1];
        final long[] sum = new long[1];
        final long[] count = new long[1];
        final int[] max = new int[1];
        final int[] min = new int[1];

        product[0] = 1L;
        max[0] = Integer.MIN_VALUE;
        min[0] = Integer.MAX_VALUE; 

        for (Integer i : integers) {
            calcSomeValues(product, sum, count, max, min, i);
        }

        System.out.println("Product :" + product[0]);   
        System.out.println("Sum :" + sum[0]);   
        System.out.println("Count:" + count[0]);
        System.out.println("Max:" + max[0]);
        System.out.println("Min:" + min[0]);            
    }

    private static void calcSomeValues(final long[] product, final long[] sum, final long[] count, final int[] max,
            final int[] min, Integer i) {
        product[0] *= i;
        sum[0] += i;
        count[0]++;
        max[0] = Math.max(max[0], i);
        min[0] = Math.min(min[0], i);
    }
}
4

1 回答 1

2

这是一个更好的方法:

public class Test {
    public static class Summary {
        private long product;
        private long sum;
        private long count;
        private int max;
        private int min;

        private Summary() {
            product = 1;
            sum = 0;
            count = 0;
            max = Integer.MIN_VALUE;
            min = Integer.MAX_VALUE; 
        }

        public long getProduct() { return product; }
        public long getSum() { return sum; }
        public int getCount() { return count; }
        public int getMax() { return max; }
        public int getMin() { return min; }

        public static Summary summarize(Collection<Integer> ints) {
            Summary s = new Summary();

            s.count = ints.size();
            for (Integer i : ints) {
                s.product *= i;
                s.sum += i;

                if (i > s.max) {
                    // You can use Math.max if you want
                    s.max = i;
                }
                if (i < s.min) {
                    // You can use Math.min if you want
                    s.min = i;
                }
            }

            return s;
        }
    }

    public static void main(String[] args) {
        final Set<Integer> integers = new HashSet<>();
        integers.add(1);
        integers.add(9);
        integers.add(8);
        integers.add(4);
        // and so on ...

        Summary s = Summary.summarize(integers);

        System.out.println("Product: " + s.getProduct());
        System.out.println("Sum: " + s.getSum());
        System.out.println("Count: " + s.getCount());
        System.out.println("Max: " + s.getMax());
        System.out.println("Min: " + s.getProduct());
    }
}

以你的方式使用数组很奇怪。不要这样做。这会使其他程序员感到困惑,并且不是该语言的预期用途。它违反了最小惊讶原则。

相反,找到一种方法让系统为您工作,而不会进入奇怪的领域。您有多个在逻辑上相互关联的值,并且它们都是同时计算的。当您有多个值一起使用时,是考虑使用类的好时机。通过使用一个类和一个方法来完成所有更新,您的代码清晰而合理。我提供的类实际上最终是不可变的(就外部代码而言),因为计算摘要的逻辑都在summarize方法内部,该方法可以访问私有属性,因此封装得很好。(名称可能更好,但我认为这足以作为示例。)如果summarize不希望修改私有状态,Summary参数及其实例变量的值,并在将它们计算为局部变量后将值简单地传递给构造函数,这将Summary变成一个非常简单的结果对象。

保持所有这些逻辑非常本地化并防止调用者修改结果使得很容易推理正在发生的事情。您的长度为 1 数组的示例代码违反了这两个原则,并且使代码更难理解、使用或维护。

或者,如果您可以在计算后立即使用这些值,则可以跳过该课程并直接计算它们。您可以通过循环执行此操作,也可以使用内置功能单独计算它们。

于 2016-12-19T00:23:56.713 回答