关于 Integer 的那些事

  今天在做 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BugFree_张瑞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值