1. Java包装类与类型转换解析
包装类是Java对基本数据类型的对象封装,位于java.lang包中。每个基本类型都有对应的包装类:
- byte → Byte
- short → Short
- int → Integer
- long → Long
- float → Float
- double → Double
- char → Character
- boolean → Boolean
1.1 自动装箱与拆箱原理
自动装箱是Java编译器在编译阶段完成的语法糖。当基本类型需要被当作对象使用时,编译器会自动插入valueOf()调用:
java复制Integer num = 10;
// 实际编译为:Integer num = Integer.valueOf(10);
拆箱则是自动调用xxxValue()方法:
java复制int n = num;
// 编译为:int n = num.intValue();
注意:包装类对象间的比较应该使用equals()而非==,因为==比较的是对象引用而非值
1.2 类型转换的三种方式
- 强制类型转换:
java复制double d = 3.14;
int i = (int)d; // i=3
- 包装类转换方法:
java复制String s = "123";
int num = Integer.parseInt(s); // 字符串转int
- Number类通用转换:
java复制Number num = new Float(3.14);
double d = num.doubleValue();
1.3 类型转换的常见问题
问题1:数字格式异常
java复制String s = "12a";
int i = Integer.parseInt(s); // 抛出NumberFormatException
解决方案:
java复制try {
int i = Integer.parseInt(s);
} catch(NumberFormatException e) {
// 处理非法输入
}
问题2:精度丢失
java复制long bigNum = 12345678901L;
int smallNum = (int)bigNum; // 可能溢出
解决方案:
java复制if(bigNum >= Integer.MIN_VALUE && bigNum <= Integer.MAX_VALUE) {
int smallNum = (int)bigNum;
}
2. Set接口及其实现类详解
Set接口继承自Collection,特点是不允许重复元素,最多包含一个null元素。
2.1 HashSet实现原理
HashSet底层使用HashMap实现,元素作为Map的key存储:
java复制private transient HashMap<E,Object> map;
// 值使用固定对象占位
private static final Object PRESENT = new Object();
特性:
- 无序(迭代顺序不保证)
- 允许null元素
- 基本操作时间复杂度O(1)
实际开发中,重写hashCode()和equals()方法对HashSet至关重要
2.2 TreeSet的排序机制
TreeSet基于TreeMap实现,支持两种排序方式:
- 自然排序(元素实现Comparable)
java复制class Person implements Comparable<Person> {
public int compareTo(Person p) {
return this.age - p.age;
}
}
- 定制排序(传入Comparator)
java复制TreeSet<String> set = new TreeSet<>(
(s1, s2) -> s2.length() - s1.length()
);
2.3 三种Set的性能对比
| 实现类 | 底层结构 | 时间复杂度 | 线程安全 | 排序 |
|---|---|---|---|---|
| HashSet | 哈希表 | O(1) | 否 | 无序 |
| LinkedHashSet | 链表+哈希表 | O(1) | 否 | 插入顺序 |
| TreeSet | 红黑树 | O(log n) | 否 | 自然/定制排序 |
3. Map接口深度解析
Map存储键值对映射,key不允许重复。
3.1 HashMap核心机制
JDK8的优化:
- 数组+链表+红黑树结构
- 链表长度>8时转红黑树
- 扩容因子默认0.75
java复制final V putVal(int hash, K key, V value) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
// 处理哈希冲突...
}
++modCount;
if (++size > threshold)
resize();
return null;
}
3.2 ConcurrentHashMap线程安全实现
JDK8改进:
- 取消分段锁,采用CAS+synchronized
- 节点锁粒度更细
- 并发度更高
java复制final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
break;
}
// ...其他情况处理
}
addCount(1L, binCount);
return null;
}
3.3 Map遍历的四种方式
- entrySet迭代(性能最优)
java复制for(Map.Entry<K,V> entry : map.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
}
- keySet遍历
java复制for(K key : map.keySet()) {
V value = map.get(key);
}
- values遍历
java复制for(V value : map.values()) {
// 只访问值
}
- forEach方法(JDK8+)
java复制map.forEach((k,v) -> System.out.println(k+"="+v));
4. 集合框架实战技巧
4.1 不可变集合创建
JDK9新方式:
java复制Set<String> set = Set.of("a", "b", "c");
Map<String,Integer> map = Map.of("a",1, "b",2);
传统方式:
java复制Set<String> set = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList("a","b"))
);
4.2 集合与数组转换
集合转数组:
java复制List<String> list = new ArrayList<>();
String[] array = list.toArray(new String[0]);
数组转集合:
java复制String[] arr = {"a","b"};
List<String> list = Arrays.asList(arr); // 固定大小
List<String> realList = new ArrayList<>(Arrays.asList(arr)); // 可变
4.3 线程安全集合方案
- Collections工具类:
java复制List<String> syncList = Collections.synchronizedList(new ArrayList<>());
- 并发集合:
java复制Map<String,String> map = new ConcurrentHashMap<>();
Queue<String> queue = new ConcurrentLinkedQueue<>();
- CopyOnWrite机制:
java复制List<String> cowList = new CopyOnWriteArrayList<>();
Set<String> cowSet = new CopyOnWriteArraySet<>();
5. 性能优化与问题排查
5.1 HashMap初始化优化
不良实践:
java复制Map<String,String> map = new HashMap<>(); // 默认大小16
for(int i=0; i<100; i++) {
map.put("key"+i, "value"); // 触发多次扩容
}
优化方案:
java复制Map<String,String> map = new HashMap<>(128); // 预估容量
计算公式:initialCapacity = (元素个数 / 负载因子) + 1
5.2 内存泄漏场景
典型case:
java复制public class Cache {
private static final Map<Object,Object> CACHE = new HashMap<>();
public void put(Object key, Object value) {
CACHE.put(key, value);
}
// 缺少remove方法...
}
解决方案:
- 使用WeakHashMap
- 定期清理机制
- 限制缓存大小
5.3 并发修改异常处理
问题代码:
java复制for(String key : map.keySet()) {
if("delete".equals(key)) {
map.remove(key); // 抛出ConcurrentModificationException
}
}
正确方式:
java复制Iterator<Map.Entry<String,String>> it = map.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<String,String> entry = it.next();
if("delete".equals(entry.getKey())) {
it.remove(); // 安全删除
}
}
6. 最佳实践总结
-
集合选择原则:
- 需要唯一性 → Set
- 需要键值对 → Map
- 需要排序 → TreeSet/TreeMap
- 需要线程安全 → ConcurrentHashMap/CopyOnWriteArrayList
-
性能敏感场景:
- 预估集合初始大小
- 避免频繁装箱拆箱
- 批量操作使用addAll/putAll
-
代码可读性:
- JDK8+推荐使用Stream API
- 使用Guava等工具类简化操作
- 复杂操作添加注释说明
java复制// 使用Stream过滤并收集
List<String> filtered = list.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
