摘要: 检索核心思路:通过合理的组织数据,尽可能的快速减少查询范围。 ①、合理选择存储介质、存储数据结构; ②、合理创建索引,使得索引和数据分离; ③、减少磁盘IO,将频繁读取的数据加载到内存中; ④、读写分离; ⑤、分层处理; 阅读全文
posted @ 2022-03-14 10:19 YSOcean 阅读(1238) 评论(0) 推荐(0) 编辑

在日常编程中,我们经常遇到一些看似简单却隐藏着复杂逻辑的问题。

比如,你是否想过为什么在 Java 中表达式1000==1000会返回 false,而 100==100 却返回 true 呢?

Integer a = 100;
Integer b = 100;
System.out.println(a == b); // 输出:true
Integer c = 1000;
Integer d = 1000;
System.out.println(c == d); // 输出:false

1、源码追溯

解决问题,一定要深入本质,而解决编程问题,深入本质的方法就是对源码一探究竟。

可能大家不知道 Integer a = 100 这种代码是看哪个源码,不要紧,我们可以看下其编译后的 class 文件。

很明显,我们得看 Integer 类的 valueOf 方法:

继续看 IntegerCache :

为了防止大家不好理解,我这里为这个方法添加了详细注释:

private static class IntegerCache {
// 缓存的下界值,固定为-128
static final int low = -128;
// 缓存的上界值,可以通过系统属性进行配置
static final int high;
// 缓存数组,用于存储从low到high范围内的Integer对象
static final Integer cache[];
static {
// 默认情况下,缓存的上界是127
int h = 127;
// 尝试从系统属性java.lang.Integer.IntegerCache.high中获取自定义的上界值
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
// 将字符串转换为整数
int i = parseInt(integerCacheHighPropValue);
// 确保自定义的上界至少为127,以包含Java规范要求的缓存范围
i = Math.max(i, 127);
// 确保上界不超过Integer.MAX_VALUE - (-low) - 1,以防止数组大小超出Integer的最大值
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// 如果字符串无法解析为整数,忽略该属性并保持默认的上界值
}
}
// 设置高界值
high = h;
// 初始化缓存数组,数组大小根据low和high计算得出
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++) {
// 创建Integer对象并填充数组
cache[k] = new Integer(j++);
}
// 断言确保缓存的上界至少为127,符合Java语言规范
assert IntegerCache.high >= 127;
}
// 私有构造器,防止外部实例化这个内部类
private IntegerCache() {}
}

2、源码解读

其实这部分源码不难理解,首先对于 valueOf 方法,当传入的整型值在 -128-127 之间时,返回的是 IntegerCache 里面的值。

这个 IntegerCache 是在 Java 的 Integer 类中的一个内部静态类 ,它缓存了 -128 到 127 之间的整数。

当我们声明一个 Integer 对象并赋予一个在这个范围内的值时,Java 实际上会返回一个预先创建好的对象引用。

这种机制可以有效减少内存的使用,并提高性能。

3、解答问题

看懂了源码,在回到上面的问题,为什么表达式1000==1000会返回 false,而 100==100 却返回 true 呢?

当我们使用 Integer 对象比较两个数时,实际上是在比较对象的内存地址。由于“100”在缓存范围内,两个“100”实际上引用的是同一个对象,所以返回 true。

相反,“1000”不在缓存范围内,即使数值相同,两个“1000”也是不同的对象,因此内存地址不同,返回 false。

4、正确比较

其实对于 Integer 这种包装类比较大小,我们应该使用 equals() 方法来比较两个 Integer 对象的数值,而不是直接使用 == 操作符,除非我们确实想比较对象的引用。

Integer a = 100;
Integer b = 100;
System.out.println(a.equals(b)); // 输出:true
Integer c = 1000;
Integer d = 1000;
System.out.println(c.equals(d)); // 输出:true

这点在阿里开发手册中也有详细说明:

如果你是一名Java开发人员,我还是比较推荐你去阅读这个手册,对我们日常编码规范还是挺有帮助的。

下载链接:https://pan.quark.cn/s/30d2c2c4239a

posted @ 2024-01-08 10:01 YSOcean 阅读(8256) 评论(39) 推荐(15) 编辑
摘要: 大家好,过去的一周,真是疯狂的一周。 GPT-4 震撼发布,拥有了多模态能力,不仅能和GPT3一样进行文字对话,还能读懂图片; 然后斯坦福大学发布 Alpaca 7 B,性能匹敌 GPT-3.5,关键是训练成本不到 600 美元,意味着我们可以更低成本使用这种模型; 接着微软王炸发布 Microso 阅读全文
posted @ 2023-03-27 11:12 YSOcean 阅读(16712) 评论(0) 推荐(0) 编辑
摘要: 很神奇的一门语言 阅读全文
posted @ 2022-09-23 10:15 YSOcean 阅读(2222) 评论(4) 推荐(7) 编辑
摘要: Rust 从入门到精通06-语句和表达式 阅读全文
posted @ 2022-09-01 11:39 YSOcean 阅读(630) 评论(0) 推荐(2) 编辑
摘要: Rust 是 静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型。 在 Rust 中,每一个值都属于某一个 数据类型(data type),分为两大类: ①、标量(scalar):整型、浮点型、布尔类型、字符类型 ②、复合(compound):元祖(tuple 阅读全文
posted @ 2022-08-15 21:57 YSOcean 阅读(601) 评论(0) 推荐(0) 编辑
摘要: 1、变量声明语法 Rust 变量必须先声明,后使用。 对于局部变量,常见是声明语法为: let variable : i32 = 100; 由于 Rust 是有自动推导类型功能的,所以后面的 :i32 是可以省略的。 1.1 语法解析更容易 局部变量声明一定是以 let 开头,类型一定是跟在冒号 : 阅读全文
posted @ 2022-08-04 11:02 YSOcean 阅读(854) 评论(2) 推荐(3) 编辑
摘要: 安装完成 Rust 之后,我们可以编写 Rust 的 Hello Word。这里介绍两种方式,一种是rust原生方式,一种是利用 cargo 工具(重要) 1、rustc 方式 1.1 创建项目目录 rust 运行不关心代码存放的目录,我们可以任意选择一个合适的位置,创建一个目录。 比如:我们创建一 阅读全文
posted @ 2022-08-02 08:12 YSOcean 阅读(874) 评论(1) 推荐(2) 编辑
摘要: 1、安装 Rustup 是rust 官方版本管理工具,安装rustup 会自动安装好 rust(还会自动安装Cargo,这是Rust 的构建系统和包管理器,很重要),所以我们只需要安装 rustup 即可。 ①、官网安装地址 https://rustup.rs/# ②、对于 Mac 或者 Linux 阅读全文
posted @ 2022-07-30 10:56 YSOcean 阅读(356) 评论(0) 推荐(0) 编辑
摘要: 连续五年在 Stack Overflow 蝉联最受欢迎的技术 阅读全文
posted @ 2022-07-28 11:36 YSOcean 阅读(1094) 评论(1) 推荐(2) 编辑
点击右上角即可分享
微信分享提示