从一道算法题看Integer类型的“常量池”

原题:https://leetcode.com/problems/min-stack/

题目本身不难,要求实现一个栈,除了有push(),pop(),top()三个基本功能外,还能够在常数时间复杂度内获得栈内的最小值。

思路很容易,维护两个栈,一个栈保存正常的数据,另一个栈的栈顶一直保持是当前的最小值,代码如下

class MinStack {

    Stack<Integer> stack = new Stack<>();
    Stack<Integer> minStack = new Stack<>();

    public void push(int x) {
        stack.push(x);
        if (minStack.size()==0 || x<=minStack.peek()) {
            minStack.push(x);
        }
    }

    public void pop() {
        if (stack.pop()==minStack.peek()){
            minStack.pop();
        }
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}

然而,wrong answer了。出错的测试用例是这样:

push(512),push(-1024),push(-1024),push(512),pop,getMin,pop,getMin,pop,getMin

正确的getMin输出结果应该是[-1024,-1024,512],但是我的结果是[-1024,-1024,-1024]

百思不得其解,代码里怎么都看不出问题在哪,不得已看了别人的答案,思路一模一样的答案里唯一不同的地方在:

public void pop() {
    int x = stack.pop();
    if (x==minStack.peek()){
        minStack.pop();
    }
}

我猜想应该是在==上面出了问题。直接调用pop()的话,返回的是一个Integer类型的对象,而正确的答案里是把返回的对象赋给了一个int变量。难道Integer类型互相之间不能用==来比较吗?但是其他的测试用例为什么又能通过呢?

经过进一步的尝试,我发现数值大于某个数的时候==就不能用了,小于的时候==能正常用,这又是为什么?

网上搜了一圈,终于搞清楚了,在Integer类的源码里有这么一段:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

可以看到有一个if判断,IntegerCache.low是写死的-128,IntegerCache.high是可以根据JVM的设置变化的,默认是127。也就是说在-128<=i<=127的范围内,取的是IntegerCache.cache这个数组里的东西,在范围以外就是一个new Integer(i)了,当然就不能用==来比较了。这个IntegerCache.cache就是传说中的常量池。网上有一段对于他的解释:

看一下IntegerCache这个类里面的内容:

private static class IntegerCache {

    private IntegerCache() {

    }

    static final Integer cache[] = new Integer[-(-128) + 127 + 1];

    static {

        for (int i = 0; i < cache.length; i++)

        cache[i] = new Integer(i - 128);

    }

}

由于cache[]在IntegerCache类中是静态数组,也就是只需要初始化一次,即static{……}部分,所以,如果Integer对象初始化时是-128~127的范围,就不需要再重新定义申请空间,都是同一个对象—在IntegerCache.cache中,这样可以在一定程度上提高效率。

回到开头的题目,我们不但可以用上面提到的答案里的写法,也可以用Java里比较两个对象的方法equals(),代码可以这样写:

public void pop() {
    if (stack.pop().equals(minStack.peek())){
        minStack.pop();
    }
}

这回终于accepted了。

还是要多提高自己的姿势水平啊!