一、字符串的“变身”艺术:StringBuffer 与 StringBuilder
我们在初学 Java 时知道 String 是不可变的。这意味着每次对 String 进行修改(如拼接),实际上都会在内存中创建一个新的对象。如果在循环中进行大量拼接,性能损耗是巨大的。
故可变字符串类出现了:
StringBuffer:它是线程安全的。如果你查看源码,会发现它的方法大多加了 synchronized 关键字。这就像是一个“带锁的笔记本”,多人同时写也不会乱,但效率相对较低。
StringBuilder:它是线程不安全的。去掉了同步锁,就像一本“敞开的笔记本”,写入速度极快。
💡 经验法则:
如果是在单线程环境下进行大量字符串拼接(比如构建 SQL 语句、拼接 HTML),首选 StringBuilder 。
只有在多线程共享同一个字符串缓冲区时,才考虑使用 StringBuffer 。
二、精准控制:DecimalFormat 小数格式化
在处理金融数据或报表展示时,我们经常需要对数字进行格式化。 DecimalFormat 是一个非常强大的工具类。
通过定义模式(Pattern),我们可以轻松控制小数的位数:
"#.00" :表示保留两位小数,不足补零。
"#,###.##" :表示千分位分隔,且最多保留两位小数。
三、Java 容器家族概览
Java 的集合框架主要分为两大类i:Collection 和 Map。
Collection 接口:这是单列数据的根接口,下面主要衍生出 List (列表)和 Set (集合)。
Map 接口:这是双列数据的根接口,用于存储键值对(Key-Value)。
- List 接口:有序的队列
List 的特点是有序且可重复。你可以把它想象成一排编了号的储物柜。
ArrayList vs LinkedList
ArrayList(数组实现):
结构:底层是动态数组。
优势:随机访问速度快。因为数组在内存中是连续的,通过索引(Index)可以直接定位元素,时间复杂度为 O(1)。
劣势:插入和删除慢。因为在中间插入一个元素,后面的所有元素都要向后移动(System.arraycopy)。
LinkedList(链表实现):
结构:底层是双向链表。
优势:插入和删除快。只需要修改指针指向即可,不需要移动元素。
劣势:随机访问慢。查找第 N 个元素必须从头遍历,时间复杂度为 O(N)。
⚠️ 避坑指南:
在使用 List 获取元素时,要注意泛型的使用。虽然 Java 早期版本允许放入 Object ,但在现代开发中,务必指定泛型(如 List
- Set 接口:独特的集合
Set 的核心特点是无序且不可重复。它主要用于去重场景。
HashSet:底层基于 HashMap 实现,利用哈希算法保证元素的唯一性。存取速度极快,但不保证顺序。
TreeSet:基于红黑树实现,可以对元素进行自然排序或自定义排序。
- Map 接口:键值对的映射
Map不是 Collection 的子接口,它是一个独立的体系。它存储的是 Key-Value 对,Key 不允许重复。
HashMap:最常用的 Map 实现。基于数组+链表(JDK8 后引入红黑树)结构。允许 null 键和 null 值,非线程安全。
TreeMap:基于红黑树,会对 Key 进行排序。
LinkedHashMap:维护了插入顺序或访问顺序。
四、实战演练:容器综合应用
在实际开发中,我们经常需要结合 Scanner 输入和容器来处理数据。
例如,一个简单的学生成绩录入系统:
使用 Scanner 读取用户输入。
使用 ArrayList 存储学生对象。
使用 HashMap 统计每个分数段的人数。
在编写这类代码时,有几个细节值得注意:
循环控制:注意 while 循环的终止条件,避免死循环。
输入校验:用户可能会输入非法字符,需要使用 try-catch 或 hasNextInt() 进行防御性编程。
遍历方式:对于 List ,推荐使用增强 for 循环(For-Each)或迭代器;对于 Map ,推荐遍历 entrySet() 以同时获取键和值,效率最高。
