欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于 Integer 的那些事

程序员文章站 2022-05-06 18:18:45
...

  今天在做 LeetCode 的113题:Path Sum II 时(题目的意思就是:输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。)

  编写的代码过了题目给的样例,但是有些样例没通过,结果输出空,和同学调了一段时间代码,总觉得代码逻辑应该没问题。仔细一想,给出的错误样例,是存在合法路径的,但是我们的代码输出空,肯定是计算和或者最终比较路径和是否与目标和相等时出现问题了。

  先附上错误代码:

import java.util.*;
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        if(root == null) return result;
        List<Integer> current = new ArrayList<>();
        int cusum = 0;
        dfs(root,current,cusum,sum);
        return result;
    }
    public void dfs(TreeNode root, List<Integer> current, Integer cusum, Integer sum)
    {
        if(root == null)
            return;
        cusum += root.val;
        current.add(root.val);
        if(root.left == null && root.right == null && sum == cusum) // 问题就出在这
        {
            result.add(new ArrayList<>(current)); 
        }
         if(root.left != null){         
        	dfs(root.left, current, cusum, sum);
        }
        if(root.right != null){
        	dfs(root.right, current, cusum, sum);
        }
        current.remove(current.size()-1);  
    }
}

  有两种解决方案:

1)将 sum == cusum 改为 sum.equals(cusum)
2)将 sum 和 cusum 的类型改为 int 型

  我在想,那究竟是为什么呢?先上测试代码 (感觉写的这个代码可以应对大部分笔试的 Integer 判断相等与否的题目了,哈哈)

public class IntegerTest {

	public static void main(String[] args) {

		Integer i01 = 59;
		int i02 = 59;
		Integer i03 = Integer.valueOf(59);
		Integer i04 = new Integer(59);

		System.out.println(i01 == i02);// (1)true

		System.out.println(i01 == i03);// (2)true

		System.out.println(i03 == i04);// (3)false

		System.out.println(i02 == i04);// (4)true
		
		Integer a = -22;
		Integer b = -22;
		System.out.println(a == b);// (5)true
		
		Integer c = 150;
		Integer d = 150;
		int dd = d.intValue();
		int cc = c.intValue();
		System.out.println(dd == cc);// (6)true
		System.out.println(c == d);// (7)false
		System.out.println(d.equals(c));// (8)true
		System.out.println(c.compareTo(d));// (9)0 代表true
		
		Integer e = new Integer(150);
		Integer f = new Integer(150);
		System.out.println(e == f);// (10)false
	}
}

  暂且先不分析上述10个输出,先考虑这个问题:Integer 为什么要提供功能与 new Integer(xx) 一样的valueOf(xx) 方法呢?看了源代码之后,你就知道了

public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
 return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i); // 缓存里没有则new
    }

  我们可以看到:原来,Integer类里定义了一个静态内部类 IntegerCache,这个方法就是返回一个 Integer 对象,只是在返回之前,看作了一个判断,判断当前 i 的值是否在 [-128,127] 区别,且 IntegerCache 中是否存在此对象,如果存在,则直接返回引用,否则,创建一个新的对象

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache;

        static {
        	// high value may be configured by property
        	int h = 127;
        	String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        	if (integerCacheHighPropValue != null) {
        		int i = parseInt(integerCacheHighPropValue);
        		i = Math.max(i, 127);
        		// Maximum array size is Integer.MAX_VALUE
        		h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        		}
        	high = h;
        	cache = new Integer[(high - low) + 1];
        	int j = low;
        	for(int k = 0; k < cache.length; k++)
        		cache[k] = new Integer(j++);
        	}
        private IntegerCache {}
    }

  可以看到这个类是私有的且是静态的,并且他有三个被 final 修饰的静态 filed 外加一个静态块和一个私有的构造器;很简单很普通的一个类,被缓存的包装类就介于 low - high 之间,low的值已经写死-128,而high的值由你的虚拟机决定,既然是一个参数也就意味着你可以动态设置,具体怎么设置自行百度。然后在循环中将 low - high 之间数字的装箱后,放在 cache[] 这个 Integer 类型的数组中。这样就完成了缓存。

  因此该类加载后会在内存里缓存若干个 Integer 对象,默认从-128~127,high可配(XX:AutoBoxCacheMax=200)。调用valueOf(x),当x>=-128且x<=high时直接取缓存(堆)里的对象,不用new,不在这个范围的则new。我们知道Integer a = 12会发生自动装箱(int->Integer),实际上是调用了Integer.valueOf(12)。

  建议:-128~127范围内的整数,尽量不要用new Integer来声明,请用自动装箱或Integer.valueOf()

  现在来依次解释测试demo中的10个输出:

1)System.out.println(i01 == i02):i01 是 Integer 对象, i02 是 int ,这里比较的不是地址,而是值。 Integer 会自动拆箱成 int ,然后进行值的比较。所以,true。
2)System.out.println(i01 == i03):因为 i03 返回的是 i01 的引用,所以,true。
3)System.out.println(i03 == i04):因为 i04 是重新创建的对象,所以 i03,i04 是指向不同的对象,所以,false。
4)System.out.println(i02 == i04): 因为 i02 是基本类型,所以此时 i04 会自动拆箱,进行值比较,所以,true。
5)System.out.println(a == b):因为a和b都在[-128,127]范围内,属于同一个引用,所以,true。
6)System.out.println(dd == cc):比较的是value值,所以,true。
7)System.out.println(c == d):根据源码,超出范围的Integer分配机制不一样,也就是不是同一个对象了,所以false。
8)System.out.println(d.equals©):比较的是两个Integer对象的值的大小,所以true。(附equals()源码)

private final int value;

public boolean equals(Object obj) {
        if (obj instanceof Integer) {
	        return value == ((Integer)obj).intValue;
        }
        return false;
    }

9)System.out.println(c.compareTo(d)):同上(8)
10)System.out.println(e == f):因为e和f是两个不同的对象,所以,false。

——乐于分享,共同进步,欢迎补充
——Any comments greatly appreciated
——诚心欢迎各位交流讨论!QQ:1138517609
——CSDN:https://blog.csdn.net/u011489043
——简书:https://www.jianshu.com/u/4968682d58d1
——GitHub:https://github.com/selfconzrr