当前位置: 首页 > news >正文

Java 中 i++ 与 ++i 的区别及常见误区解析

在 Java 编程中,我们经常会用到自增操作符:i++(后置递增) 和 ++i(前置递增)。虽然它们看起来很相似,但在执行顺序和返回值上存在显著差异。理解这些差异可以帮助我们避免一些常见的错误,尤其是在处理复杂表达式或性能敏感场景时。

一、问题引入:HashMap 统计字符出现次数的案例

举个简单的例子,我们希望统计一个长字符串中每个字符出现的次数,并使用 HashMap<Character, Integer> 来实现:

String p = "abcdabcd";
HashMap<Character, Integer> map = new HashMap<>();for (int i = 0; i < p.length(); i++) {int count = map.getOrDefault(p.charAt(i), 0);map.put(p.charAt(i), count++);
}

❗运行结果:

{'a': 0, 'b': 0, 'c': 0, 'd': 0}

❓为什么会这样?

问题出在这一行代码:

map.put(p.charAt(i), count++);

这里使用的是 后置递增操作符 count++,它的行为是:

  • 先将当前 count 的值作为表达式的返回值;
  • 然后再对 count 自增。

也就是说,map.put(...) 实际上传入的是原来的 count 值(例如 0),而 count 虽然增加了,但并没有被再次写回 map

✅ 正确写法应该是:

map.put(p.charAt(i), ++count); // 或者先 count += 1,再 put

或者更清晰的方式:

map.put(p.charAt(i), count + 1);

二、前置递增 ++i 与后置递增 i++ 的核心区别

特性i++(后置递增)++i(前置递增)
表达式返回值返回原始值返回自增后的新值
执行顺序先使用原值,再自增先自增,再使用新值

示例说明:

int i = 5;
int a = i++;  // a = 5,i = 6
int i = 5;
int b = ++i;  // i = 6,b = 6

三、运算优先级与表达式陷阱

示例一:

int i = 3;
System.out.println(i++ * 2);  // 输出 6(因为 i 是 3)
int j = 3;
System.out.println(++j * 2);  // 输出 8(因为 j 先变为 4)

示例二(不推荐):

int i = 0;
int x = i++ + ++i;  // 不同编译器可能结果不同,而且难以阅读

⚠️ 这类复合表达式容易引发歧义,建议拆分成多个语句以提高可读性和可维护性。


四、性能差异分析

对于基本类型(如 int):

在大多数现代 JVM 实现中,i++++i 在循环中的性能几乎一致,甚至生成的字节码完全相同:

for (int i = 0; i < 10; i++) { ... }   // 可能等效于 ++i

对于对象类型(如迭代器或自定义类):

  • obj++:需要创建临时对象来保存原始值,可能会调用拷贝构造函数,带来额外开销。
  • ++obj:直接修改对象本身,效率更高。

✅ 推荐:在涉及对象或自定义类型的自增操作中,优先使用 ++obj

这也是为什么很多 LeetCode 题解中倾向于使用 ++i 而不是 i++


五、注意事项与最佳实践

场景建议
简单变量自增i++ 和 ++i 性能无明显差异
复杂表达式中避免混合使用 i++ 和 ++i,容易造成逻辑混乱
自定义类型或迭代器使用 ++i 更高效
可读性要求高时明确写出逻辑,比如 i = i + 1
循环控制变量使用 ++i 更符合“先自增再使用”的语义

六、总结一句话记忆口诀

"i在前是原值,i在后先计算"

即:

  • i++:表达式中使用的是 i 的原始值;
  • ++i:表达式中使用的是 i 的新值。

七、附表对比

操作符表达式返回值是否立即自增适用场景
i++原始值否(之后)需保留旧值参与运算
++i新值需要立刻使用更新后的值进行操作

文章转载自:

http://thaCaDmW.qfrmy.cn
http://6PmoUET2.qfrmy.cn
http://M9GsNhtF.qfrmy.cn
http://EZigTETp.qfrmy.cn
http://SAoaQBEZ.qfrmy.cn
http://LcTFFP6I.qfrmy.cn
http://MWSkxtcC.qfrmy.cn
http://PO3X5vDC.qfrmy.cn
http://TrEMarYb.qfrmy.cn
http://V9a3v0ba.qfrmy.cn
http://2IcT84Fi.qfrmy.cn
http://ZGeh7YGc.qfrmy.cn
http://uxDHsxpa.qfrmy.cn
http://co22I99d.qfrmy.cn
http://8A3NEpKl.qfrmy.cn
http://OtrfPkNv.qfrmy.cn
http://dVVc3LFw.qfrmy.cn
http://9iFQN2dY.qfrmy.cn
http://mom9ahkD.qfrmy.cn
http://aysLPKvk.qfrmy.cn
http://ZTcrQJ0c.qfrmy.cn
http://seEwtp1c.qfrmy.cn
http://t9izpsnQ.qfrmy.cn
http://tACRgZTu.qfrmy.cn
http://vji25Y8A.qfrmy.cn
http://Sts1w9fU.qfrmy.cn
http://qEa741cU.qfrmy.cn
http://EZ5TorWw.qfrmy.cn
http://jNwXN7eb.qfrmy.cn
http://fkldd458.qfrmy.cn
http://www.dtcms.com/a/229204.html

相关文章:

  • 《Effective Python》第六章 推导式和生成器——使用类替代生成器的 `throw` 方法管理迭代状态转换
  • 行列式详解:从定义到应用
  • C++的多态特性及private
  • Go的隐式接口机制
  • Vue中安装插件的方式
  • 技巧小结:外部总线访问FPGA寄存器
  • 登高架设作业实操考试需要注意哪些安全细节?
  • 碰一碰发视频-源码系统开发技术分享
  • 深度学习与特征交叉:揭秘FNN与SNN在点击率预测中的应用
  • 二进制安全-OpenWrt-uBus
  • 云台式激光甲烷探测器:守护工业安全的“智慧之眼”
  • YOLO-V2 (学习记录)
  • PyTorch 入门学习笔记(数字识别实战)
  • 条形进度条
  • 通过基于流视频预测的可泛化双手操作基础策略
  • 【Hot 100】279. 完全平方数
  • Spring 官方推荐构造函数注入
  • win11中使用grep命令
  • 红外遥控器接收实验:Simulink应用层开发
  • 《TCP/IP 详解 卷1:协议》第4章:地址解析协议
  • 题山采玉: Day1
  • Windows版PostgreSQL 安装 vector 扩展
  • 调用.net DLL让CANoe自动识别串口号
  • 数据库完整性
  • 【吾爱】逆向实战crackme160破解记录(二)
  • 从“remote rejected”看git角色区别,Maintainer和Devoloper
  • GaLore:基于梯度低秩投影的大语言模型高效训练方法详解一
  • 开发体育比分平台,有哪些坑需要注意的
  • 深入对比主流Java Web服务器与框架
  • 前端​​HTML contenteditable 属性使用指南