关于 java:3. Java 常用类库与数据结构

发布于:2025-06-04 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、String

1.1 String 是什么?

public final class String implements java.io.Serializable, Comparable<String>, CharSequence

特点:

  • 不可变对象(immutable)

  • final 类,不能被继承

  • 内部使用 字符数组存储内容

  • 重写了 equals()hashCode()toString() 等方法

  • 实现了 ComparableCharSequence 接口

1.2 不可变特性详解

String s1 = "abc";
s1 = s1 + "d";
System.out.println(s1); // abcd
  • 本质上:s1 + "d" 生成了一个新对象 "abcd"s1 引用被修改,原 "abc" 没变

  • 不可变的好处:

    • 线程安全

    • 可以缓存 hash 值(提高查找效率)

    • 可以作为 HashMap 的 key(哈希值不变)

1.3 内存结构与字符串常量池

String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true
  • "hello" 存在字符串常量池(方法区/元空间)

  • 相同字面量指向同一个对象,节省内存

String s3 = new String("hello");
System.out.println(s1 == s3); // false
  • new 会强制在堆上创建新对象,== 比较的是地址,不相等

intern() 方法

String s = new String("abc").intern();
  • 将字符串加入常量池并返回池中对象的引用

  • 常用于避免重复字符串

1.4 底层实现与源码分析(JDK 8)

private final char value[];
  • 每个 String 对象内部有一个 char[],一旦创建就不能变

  • JDK 9+ 改为使用 byte[] + 编码标识,提升效率

1.5 常用方法分类

>判断类方法

s.equals("abc")       // 是否相等
s.equalsIgnoreCase()  // 忽略大小写比较
s.contains("abc")     // 是否包含子串
s.startsWith("ab")    // 是否以 ab 开头
s.endsWith("bc")      // 是否以 bc 结尾

>查找/提取类方法

s.indexOf("a")         // 第一个 a 出现的位置
s.lastIndexOf("a")     // 最后一个 a 的位置
s.charAt(2)            // 获取第 3 个字符
s.substring(1, 4)      // 截取 [1,4) 子串

>替换/处理类方法

s.replace("a", "b")     // 替换
s.trim()                // 去掉首尾空格
s.toUpperCase()         // 转大写
s.toLowerCase()         // 转小写
s.split(",")            // 拆分字符串为数组

>转换类方法

String.valueOf(123)     // 任意类型转字符串
Integer.parseInt("123") // 字符串转 int
s.toCharArray()         // 字符串转字符数组

1.6 字符串拼接与性能分析

String result = "";
for (int i = 0; i < 1000; i++) {
    result += i;
}

上面每次 += 都会创建新对象,性能低下。

推荐使用:StringBuilder

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result = sb.toString();

1.7 字符串比较

String s1 = "abc";
String s2 = new String("abc");

System.out.println(s1 == s2);         // false(引用不同)
System.out.println(s1.equals(s2));    // true(内容相同)
  •  == 比较引用
  • equals() 比较内容
  • compareTo() 用于排序(按 Unicode)

1.8 String 在集合中做 key?

  • String 是不可变的,hashCode 不变,适合作为 HashMap 的 key。

  • equals()hashCode() 都已重写,保证内容相等时哈希一致。

1.9 常见问题

问题 简要回答
String 为什么不可变? 提高安全性、支持缓存、可作为 Hash key、线程安全
==equals() 区别? == 比地址,equals 比内容
字符串拼接性能问题? 多次拼接用 StringBuilder
intern() 的作用? 把字符串加入常量池,返回常量池中地址
字符串常量池在哪里? JDK 8 在方法区(元空间),JDK 7+ 移至堆
String 能被继承吗? 不能,是 final 类
String 为什么能作为 Map 的 key? 不可变、重写了 hashCode 和 equals
new String("abc") 创建了几个对象? 常量池中 1 个("abc"),堆中又 1 个(new)

1.10 小结

特性 说明
不可变 修改就生成新对象,安全,支持哈希
常量池优化 相同字面量只创建一次
内部结构 JDK 8 是 char[],JDK 9 是 byte[] + 编码
性能优化建议 多次拼接用 StringBuilder
与其他类区别 与 StringBuilder / Buffer 的线程与性能区别

二、StringBuilder

2.1 什么是 StringBuilder?

public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence

特点:

  • 可变字符串(mutable)

  • 线程不安全(但性能更高)

  • 使用字符数组维护可变字符串内容

  • 用于大量拼接字符串时的性能优化

2.2 StringBuilder 与 String 的区别

特性 String StringBuilder
是否可变 不可变(immutable) 可变
线程安全 安全 不安全
拼接效率 低(频繁创建新对象) 高(内部数组直接修改)
底层结构 char[] char[](可扩容)
用途 小量拼接、常量字符串 频繁拼接、大量字符串操作

2.3 常用构造函数

StringBuilder sb1 = new StringBuilder();          // 默认容量16
StringBuilder sb2 = new StringBuilder("hello");   // 初始化内容
StringBuilder sb3 = new StringBuilder(100);       // 指定初始容量

2.4 常用方法

方法 作用
append() 添加内容到末尾
insert(index, str) 在指定位置插入字符串
delete(start, end) 删除区间内容(左闭右开)
deleteCharAt(index) 删除指定位置字符
replace(start, end, s) 替换某一段字符串
reverse() 反转字符串
toString() 转成 String 对象
setCharAt(index, ch) 修改指定位置字符
charAt(index) 读取指定位置字符
length() 当前长度
capacity() 当前容量(可容纳的最大字符数)
ensureCapacity(int) 手动增加容量
trimToSize() 减少容量到实际长度

2.5 append 示例:高性能拼接

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i).append(",");
}
String result = sb.toString();
  • append() 会修改原数组,不创建新对象,效率远高于 String +=

2.6 底层原理:char[] + 自动扩容

// JDK 8 中
char[] value;
int count;
  • 初始容量为 16,超过就自动扩容

  • 每次扩容为:newCapacity = oldCapacity * 2 + 2

void expandCapacity(int minimumCapacity) {
    int newCapacity = (value.length << 1) + 2;
    if (newCapacity < minimumCapacity) {
        newCapacity = minimumCapacity;
    }
    value = Arrays.copyOf(value, newCapacity);
}

2.7 扩容机制详解

默认构造:

StringBuilder sb = new StringBuilder();
  • 初始化时创建 char[16] 数组

  • 一旦内容超过 16 个字符,就扩容为 16*2+2 = 34

  • 后续继续超过,又变为 34*2+2 = 70,以此类推

2.8 线程安全性对比:StringBuffer

类名 可变 线程安全 性能 使用场景
String 最低 字符串不变或少量拼接
StringBuilder 最高 单线程、大量拼接
StringBuffer 中等 多线程环境拼接字符串

2.9 常见问题

问题 解答说明
StringBuilder 适合什么场景? 在循环中频繁拼接字符串时使用
append 是怎么实现的? 直接往 char[] 数组写,满了就扩容
线程安全吗? 不安全(非同步),多线程环境用 StringBuffer
初始容量是多少? 默认 16,构造函数可指定
为什么效率比 String 高? 不创建新对象,原地修改数组,减少内存和 CPU 开销

2.10 实践建议

建议 说明
多次拼接请用 StringBuilder 尤其是循环中拼接字符串
如果知道大致容量,建议指定容量构造函数 减少扩容次数,提高效率
单线程下推荐使用 StringBuilder 比 StringBuffer 快很多
多线程共享字符串拼接建议用 StringBuffer 线程安全但稍慢

2.11 示例代码

public class Demo {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("abc");
        sb.append(123).append(true);
        System.out.println(sb); // abc123true

        sb.insert(3, "XYZ");
        System.out.println(sb); // abcXYZ123true

        sb.delete(3, 6);
        System.out.println(sb); // abc123true

        sb.replace(3, 6, "___");
        System.out.println(sb); // abc___true

        sb.reverse();
        System.out.println(sb); // eurt___cba

        System.out.println(sb.capacity()); // 查看当前容量
    }
}

三、StringBuffer

3.1 什么是 StringBuffer

public final class StringBuffer extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

特点总结:

  • 可变字符串类(mutable)

  • 线程安全:方法加了 synchronized

  • 底层使用 char[] 存储字符串

  • 实现了 CharSequence 接口,可用于 for-each

3.2 StringBuffer 与其他类对比

类名 可变 线程安全 适合场景 性能
String 不可变字符串、少量拼接 最低
StringBuilder 单线程环境拼接字符串 最快
StringBuffer 多线程环境拼接字符串 较快(比 StringBuilder 慢)

3.3 常用构造方法

StringBuffer sb1 = new StringBuffer();               // 默认容量 16
StringBuffer sb2 = new StringBuffer("hello");        // 初始内容
StringBuffer sb3 = new StringBuffer(100);            // 指定容量

3.4 常用方法和功能

添加内容(append)

sb.append("abc");         // 添加字符串
sb.append(123);           // 添加数字
sb.append(true);          // 添加布尔值

插入内容(insert)

sb.insert(1, "xyz");      // 在位置1插入 "xyz"

删除内容(delete)

sb.delete(3, 6);          // 删除从3到6(左闭右开)
sb.deleteCharAt(2);       // 删除第2个字符

替换、反转、设置

sb.replace(1, 4, "ZZZ");  // 替换子串
sb.reverse();             // 反转整个字符串
sb.setCharAt(0, 'X');     // 修改第一个字符

长度与容量

sb.length();              // 当前实际长度
sb.capacity();            // 当前 char[] 容量(默认16)
sb.ensureCapacity(50);    // 预设容量,避免扩容

转换为 String

String s = sb.toString(); // 获取最终字符串

3.5 线程安全实现原理

所有核心方法都加了 synchronized,示例源码如下:

public synchronized StringBuffer append(String str) {
    super.append(str);
    return this;
}
  • 多线程环境中,同一个 StringBuffer 实例的操作是互斥的,防止数据错乱。

  • 缺点是性能稍慢。

3.6 扩容机制(与 StringBuilder 相同)

  • 初始容量是 16。

  • 如果容量不足,会扩容为:

newCapacity = oldCapacity * 2 + 2;

底层使用的数组方式(JDK 8):

char[] value;

3.7 性能对比总结

单线程下:

String str = "";
for (int i = 0; i < 10000; i++) {
    str += i; // 最慢(频繁创建新对象)
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
    sb.append(i); // 推荐,最快
}
StringBuffer sbf = new StringBuffer();
for (int i = 0; i < 10000; i++) {
    sbf.append(i); // 稍慢,线程安全
}

3.8 常见问题

问题 回答简要说明
StringBufferStringBuilder 区别? Buffer 线程安全,Builder 非线程安全
为什么 StringBuffer 慢? 方法加了 synchronized,存在锁竞争
StringBuffer 初始容量是多少? 默认是 16 字符长度
能否替代 String 不能,String 是不可变类,适合做常量、Map key
适合的使用场景? 多线程环境下的大量字符串拼接

3.9 示例代码

public class BufferDemo {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Hello");

        sb.append(" World");                // 拼接
        sb.insert(5, ",");                  // 插入
        sb.replace(0, 5, "Hi");             // 替换
        sb.deleteCharAt(2);                 // 删除字符
        sb.reverse();                       // 反转

        System.out.println(sb.toString());  // 输出结果
        System.out.println("Length: " + sb.length());
        System.out.println("Capacity: " + sb.capacity());
    }
}

3.10 小结

特性 说明
可变性 内容可变,支持追加、插入、替换
线程安全性 所有方法加了 synchronized,适合并发场景
拼接效率 String 高,单线程略低于 StringBuilder
扩容机制 自动扩容为 old * 2 + 2
推荐场景 多线程拼接字符串(如日志缓冲区、消息构建)

四、List(ArrayList、LinkedList)

4.1 List 接口简介

基本定义

public interface List<E> extends Collection<E>

特点:

  • 有序集合(元素按插入顺序排列)

  • 允许重复元素

  • 可以通过索引(index)访问元素

  • 提供大量用于插入、删除、替换的操作方法

4.2 常用方法

方法 说明
add(E e) 添加元素到末尾
add(int index, E e) 指定位置插入
remove(int index) 删除指定位置元素
get(int index) 获取指定位置元素
set(int index, E e) 替换指定位置的元素
indexOf(Object o) 返回首次出现的索引
contains(Object o) 是否包含某个元素
size() 集合大小
clear() 清空集合

4.3 List 的两大核心实现类对比:ArrayList vs LinkedList

特性 ArrayList LinkedList
底层结构 动态数组(Object[]) 双向链表(Node)
查询效率 高(O(1)) 低(O(n))
插入/删除效率 中(尾部快,中间慢,O(n)) 高(O(1) 位置已知)
内存使用 少(数组连续内存) 多(存指针结构)
线程安全
适合场景 多查询、少插入/删除 多插入/删除、少查询

4.4 ArrayList 详解

构造函数

List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>(100); // 指定初始容量

底层实现

  • 内部使用 Object[] 数组存储元素

  • 容量不够时自动扩容(扩容机制为原容量的 1.5倍

  • 随机访问元素效率高:list.get(i) 时间复杂度 O(1)

添加元素

list.add("A");           // 添加到末尾
list.add(1, "B");        // 插入到下标1

删除元素

list.remove(0);          // 删除指定下标
list.remove("A");        // 删除首次匹配元素

查询元素

list.get(0);             // 获取第一个元素
list.contains("X");      // 是否存在

遍历

for (String s : list) { System.out.println(s); }

4.5 LinkedList 详解

构造函数

List<String> list = new LinkedList<>();

底层实现

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
}
  • 每个元素是一个节点,节点包含指向前一个、后一个的引用

  • 插入、删除元素时只需改指针,不需要数组拷贝

  • 访问指定位置时,需要从头/尾顺序遍历,效率低

特有方法(作为队列和栈)

方法 类型 说明
addFirst() 栈/队列 从头部添加元素
addLast() 队列 从尾部添加元素
removeFirst() 栈/队列 移除头部元素
removeLast() 队列 移除尾部元素
peekFirst() 队列/栈 查看头部元素但不删除

4.6 应用场景对比总结

使用场景 推荐使用的实现类
查询操作频繁 ArrayList(效率高)
插入/删除操作频繁 LinkedList(无需位移)
实现队列(FIFO) LinkedList
实现栈(LIFO) LinkedList
数据结构较小,无需考虑性能 任意都可以

4.7 实战示例

import java.util.*;

public class ListDemo {
    public static void main(String[] args) {
        // ArrayList 示例
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Java");
        arrayList.add("Python");
        arrayList.add("C++");
        System.out.println("ArrayList: " + arrayList);

        // LinkedList 示例(队列)
        LinkedList<String> linkedList = new LinkedList<>();
        linkedList.addFirst("A");
        linkedList.addLast("B");
        linkedList.add("C"); // 默认加在尾部
        System.out.println("LinkedList: " + linkedList);

        linkedList.removeFirst();
        System.out.println("After removeFirst: " + linkedList);
    }
}

4.8 常见问题

问题 简要解答
ArrayList 和 LinkedList 区别? 底层结构、增删查性能差异
ArrayList 如何扩容? 原容量的 1.5 倍(newCapacity = old + old >> 1
LinkedList 插入为什么效率高? 改链表指针即可,无需数组移动
ArrayList 删除中间元素性能差在哪里? 后续元素要整体前移,O(n)
是否线程安全? 默认都不是,需使用 Collections.synchronizedList

4.9 线程安全的 List

List<String> safeList = Collections.synchronizedList(new ArrayList<>());

或者使用 JDK 1.5 之后的:

List<String> copyOnWriteList = new CopyOnWriteArrayList<>();

适用于读多写少的并发场景。


五、Map(HashMap、TreeMap、LinkedHashMap)

5.1 Map 接口简介

特点:

  • 存储的是「键值对 key-value

  • key 不可重复,value 可重复

  • 常用于:缓存、对象映射、参数传递等

  • 提供查询、添加、删除、遍历等操作

5.2 常用 Map 实现类对比

实现类 底层结构 是否有序 是否线程安全 是否允许 null key/value 排序方式
HashMap 数组 + 链表 + 红黑树(JDK8)  无序  允许 无顺序
LinkedHashMap HashMap + 双向链表  插入顺序  允许 按插入顺序
TreeMap 红黑树(Tree)  排序  不允许 null key 按 key 自然顺序 或 Comparator

5.3 Map 常用方法

方法 说明
put(K key, V value) 添加或替换键值对
get(K key) 根据 key 获取 value
remove(K key) 删除指定 key 的项
containsKey(K key) 是否包含某个 key
containsValue(V value) 是否包含某个 value
keySet() 获取所有 key 的集合
values() 获取所有 value 的集合
entrySet() 获取所有键值对(Map.Entry)集合
size() 获取元素个数

5.4 HashMap 详解

底层结构(JDK 1.8)

Node<K,V>[] table; // 数组
  • 每个桶(table[i])是一个链表或红黑树

  • 默认初始容量:16

  • 负载因子:0.75(容量达到 75% 时触发扩容)

  • 触发红黑树条件:

    • 链表长度 ≥ 8 且 table.length ≥ 64,才转换为红黑树

    • 否则仍使用链表(节省空间)

put 原理简述:

hash(key) -> 计算 hash 值
index = hash & (table.length - 1) -> 计算桶位置
插入链表 or 红黑树 or 替换已有 key 的值

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
System.out.println(map.get("A")); // 输出 1

5.5 LinkedHashMap 详解

特点:

  • 保持插入顺序(也可配置为访问顺序)

  • 基于 HashMap + 双向链表 实现

  • 常用于实现 LRU 缓存

结构图示:

HashMap 提供存取性能
双向链表维护元素顺序

LRU 缓存示例

LinkedHashMap<String, String> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > 100;
    }
};

5.6 TreeMap 详解(有序)

特点:

  • 内部基于红黑树实现

  • 元素按 key 的自然顺序Comparator 指定顺序排序

  • 查询、插入、删除复杂度为 O(log n)

适合场景:

  • 要求 key 有序,如:排行榜、时间线

  • 需要按区间查询数据

示例代码:

TreeMap<Integer, String> treeMap = new TreeMap<>();
treeMap.put(3, "C");
treeMap.put(1, "A");
treeMap.put(2, "B");
System.out.println(treeMap); // 按 key 升序输出

5.7 三者使用场景对比

场景 推荐 Map 实现类
快速存取、无序 HashMap
保留插入顺序 LinkedHashMap
要求 key 排序或区间查询 TreeMap
实现 LRU 缓存 LinkedHashMap(访问顺序)
线程安全要求 ConcurrentHashMap(推荐)

5.8 线程安全 Map 替代品

类名 特点
Hashtable 线程安全,方法加 synchronized,效率低
Collections.synchronizedMap 包装同步 Map,对每个方法加锁
ConcurrentHashMap 高并发环境下的 Map,分段锁或 CAS,更高性能

5.9 遍历方式

// 遍历键值对
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

// 遍历 key
for (String key : map.keySet()) {
    System.out.println(key);
}

// 遍历 value
for (Integer value : map.values()) {
    System.out.println(value);
}

5.10 常见问题

问题 解答简要
HashMap 如何解决哈希冲突? 链地址法(链表)+ JDK8 后转为红黑树
HashMap 扩容机制? 扩容为原容量的2倍,重新计算 hash 和索引
HashMap 的线程不安全性体现? 并发 put 可能导致死循环、数据丢失等
HashMap 和 TreeMap 区别? HashMap 无序,TreeMap 有序(红黑树)
LinkedHashMap 如何实现 LRU? 构造函数设为访问顺序 + 重写 removeEldestEntry
HashMap 初始容量设多少最合适? 预计大小 / 0.75(负载因子)

5.11 小结

实现类 底层结构 是否有序 主要用途
HashMap 数组 + 链表/红黑树 无序 常规场景,性能好
LinkedHashMap HashMap + 双向链表 插入序 顺序访问、LRU 缓存等
TreeMap 红黑树 排序 有序数据存储、区间查询

六、Set(HashSet、TreeSet)

6.1 Set 接口简介

特点:

  • 元素不可重复(底层通过比较是否相等来判断)

  • 不保证元素顺序(除非用 TreeSet

  • 适用于去重、集合运算、判重等场景

6.2 Set 常用实现类对比

实现类 底层结构 是否有序 是否线程安全 null 是否允许 排序依据
HashSet HashMap 实现  无序 允许一个 null 无序(依赖 hash 值)
TreeSet TreeMap(红黑树) 有序 不允许 null 按照 Comparable 或 Comparator

6.3 HashSet 详解

底层原理

  • HashSet 底层使用了一个 HashMap 来实现,值存在 Map 的 key 里

  • 本质:HashSet<E> set = new HashSet<>(); 其实是 Map<E, Object> map = new HashMap<>();

特点:

  • 插入顺序不保证

  • 元素唯一性通过 hashCode()equals() 决定

  • 插入效率高,适合查重、快速去重

示例:

Set<String> set = new HashSet<>();
set.add("A");
set.add("B");
set.add("A");  // 被忽略,不重复
System.out.println(set); // [A, B],无序

重要方法:

方法 作用
add(E e) 添加元素(若重复返回 false)
remove(E e) 删除元素
contains(E e) 判断元素是否存在
size() 获取元素数量
clear() 清空集合
isEmpty() 判断是否为空

6.4 TreeSet 详解(排序用)

底层原理:

  • TreeSet 是基于 TreeMap 实现的,内部维护的是一棵红黑树

  • 元素自动排序(升序)

特点:

  • 有序:默认按自然顺序(Comparable),也可自定义排序器(Comparator)

  • 查询/插入/删除时间复杂度为 O(log n)

  • 不允许添加 null 元素

示例:

Set<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(1);
treeSet.add(3);
System.out.println(treeSet); // [1, 3, 5],有序

自定义排序:

Set<String> sortedSet = new TreeSet<>((a, b) -> b.compareTo(a)); // 降序
sortedSet.add("apple");
sortedSet.add("banana");
System.out.println(sortedSet); // [banana, apple]

6.5 Set 相关问题

1)Set 为什么不能存重复元素?

因为底层在 HashSet 中是使用 hashCode()equals() 来判断两个元素是否“相同”。一旦认为相同,新的元素就不会被加入。重点:重写 equals 一定要同时重写 hashCode

2)HashSet 添加自定义对象无效,怎么解决?

class Person {
    String name;
    int age;

    // 忘记重写 equals 和 hashCode 会导致 set 无法正确去重
}
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    return age == person.age && name.equals(person.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

3)TreeSet 是否能存 null?

  • 不可以!

  • 插入 null 会抛出 NullPointerException(因为无法与其他元素进行比较)

4)Set 如何实现排序功能?

  • 使用 TreeSet(默认升序)

  • 或者使用 List + Collections.sort() 来手动排序

6.6 遍历方式(适用于所有 Set)

Set<String> set = new HashSet<>();
set.add("A");
set.add("B");

// 方式1:增强 for 循环
for (String s : set) {
    System.out.println(s);
}

// 方式2:迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

6.7 Set vs List 对比

特点 Set List
是否可重复
是否有序 TreeSet 有序,HashSet 无序 有序(ArrayList)
查询性能 TreeSet 慢于 HashSet ArrayList 查询快
应用场景 去重、集合运算 排序、位置访问操作

6.8 小结

实现类 底层结构 是否排序 是否允许 null 典型用途
HashSet HashMap 无序 允许一个 去重、无序集合
TreeSet 红黑树(TreeMap) 自动排序 不允许 有序集合、排序需求

七、Collection

7.1 Collection 简介

定义:Collection 是 Java 集合框架中最基本的接口,是 List、Set、Queue 的父接口。用于存储一组对象(元素),是单值集合的顶层接口(不同于 Map)。

public interface Collection<E> extends Iterable<E>

常用子接口:

          Collection
             ↑
     ┌───────┼────────┐
     ↓       ↓        ↓
   List     Set     Queue

7.2 Collection 的常用方法

方法 说明
boolean add(E e) 添加元素
boolean addAll(Collection c) 添加集合中的所有元素
void clear() 清空集合
boolean contains(Object o) 是否包含某元素
boolean containsAll(Collection c) 是否包含所有元素
boolean isEmpty() 是否为空集合
Iterator<E> iterator() 返回迭代器
boolean remove(Object o) 删除指定元素
boolean removeAll(Collection c) 删除所有与集合相同的元素
boolean retainAll(Collection c) 取交集(只保留同时存在的)
int size() 获取元素个数
Object[] toArray() 转为 Object 数组
<T> T[] toArray(T[] a) 转为指定类型数组

7.3 Collection 常见使用示例

Collection<String> coll = new ArrayList<>();
coll.add("apple");
coll.add("banana");

System.out.println(coll.contains("apple"));  // true
System.out.println(coll.size());            // 2

for (String item : coll) {
    System.out.println(item);
}

coll.remove("banana");
System.out.println(coll);  // [apple]

7.4 Collection 接口 VS Arrays

对比项 Collection 接口 数组(Array)
大小 可变 固定大小
类型 存储对象 基本类型或对象
方法支持 有大量操作方法 无操作方法,结构简单
性能 较低(要平衡功能) 高(但功能有限)

7.5 Collection 接口的实现体系

接口/类 类型 特点/说明
List 接口 有序、可重复元素
Set 接口 无序、不可重复元素
Queue 接口 队列,先进先出
Deque 接口 双端队列
ArrayList List 实现 动态数组结构,查询快
LinkedList List & Deque 实现 链表结构,增删快
HashSet Set 实现 无序、基于哈希表
TreeSet Set 实现 有序、基于红黑树
PriorityQueue Queue 实现 优先队列,可自定义优先级

7.6 增强 for 和 Iterator 遍历

Collection<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");

// 增强 for
for (String lang : list) {
    System.out.println(lang);
}

// Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

7.7 与泛型结合使用

避免强制类型转换,提高代码安全性与可读性:

Collection<Integer> nums = new ArrayList<>();
nums.add(10);
nums.add(20);

for (int num : nums) {
    System.out.println(num);
}

7.8 常见问题

Collection 与 Collections 区别?

名称 类型 说明
Collection 接口 是集合类的顶层接口,用于存储一组对象
Collections 工具类 提供各种集合相关的静态方法,如排序、复制、线程安全包装等

为什么 Collection 接口没有 get(int index) 方法?

因为不是所有集合都有顺序(如 Set),因此不提供按下标访问的统一定义。只有 List(有序)提供此能力。

7.9 小结

特点 说明
接口层级 Collection 是 List、Set 的父接口
主要功能 单值集合(增删查遍历等)
常见实现类 ArrayList, LinkedList, HashSet 等
与 Map 区别 Collection 只能存单值,Map 是键值对
与数组对比 集合功能强大,数组结构简单

八、Iterator

8.1 什么是 Iterator?

定义:Iterator 是一个迭代器接口,用于遍历集合中的元素。它是 Collection 接口的一个成员方法返回的对象。

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove(); // 可选操作
}

8.2 Iterator 能做什么?

方法 作用
hasNext() 判断是否还有下一个元素
next() 返回下一个元素
remove() 删除当前迭代到的元素(调用 next() 后才能调用)

8.3 常见使用方式

List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String lang = iterator.next();
    System.out.println(lang);
}

8.4 与增强 for 的区别

特点 增强 for Iterator
语法简洁 否,需显式声明
遍历安全性 否,不能安全删除元素 是,可以安全删除元素
适用范围 Iterable 接口(如 Collection) 同样适用
是否能修改元素 是 可通过 remove() 删除

8.5 使用 Iterator 删除元素(正确方式)

不要在增强 for 中删除元素!会报 ConcurrentModificationException

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
    int num = it.next();
    if (num == 2) {
        it.remove(); // 安全删除
    }
}
System.out.println(list); // [1, 3]

8.6 fail-fast机制(快速失败)

现象:如果在使用 Iterator 遍历集合时,集合结构被外部修改(非通过 Iterator.remove()),就会抛出:

java.util.ConcurrentModificationException

示例(错误写法):

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");

for (String s : list) {
    if (s.equals("b")) {
        list.remove(s); // 修改结构,触发 fail-fast
    }
}

正确做法:

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if (it.next().equals("b")) {
        it.remove(); // 安全删除
    }
}

原因分析:ArrayListHashSet 等集合中维护了一个 modCount 字段,记录结构修改次数。Iterator 内部也保存了一个期望的 expectedModCount,两者不一致就抛异常。

8.7 Iterable 接口与 for-each 本质

Collection 实现了 Iterable:

public interface Iterable<T> {
    Iterator<T> iterator();
}

所以增强 for 本质上就是调用了 iterator()

for (String s : list) {
    System.out.println(s);
}
// 相当于:
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
    String s = it.next();
    System.out.println(s);
}

8.8 使用场景总结

场景 是否适合使用 Iterator
遍历元素
删除元素 是(比 for 循环更安全)
随机访问(按下标) 否(用 List 的 get 更合适)

8.9 与 ListIterator 区别(只用于 List)

方法 Iterator ListIterator
支持向前遍历 不支持 支持 hasPrevious()previous()
支持添加元素 不支持 支持 add()
支持替换元素 不支持 支持 set()
适用集合 Collection 只能用于 List

8.10 小结

关键点 内容
核心方法 hasNext()next()remove()
删除方式 推荐使用 Iterator.remove()
fail-fast 非法修改集合结构会抛异常
遍历替代方式 增强 for(简单,但不能安全删除元素)
适配接口 CollectionMapkeySet()/entrySet()

九、泛型

9.1 什么是泛型?

泛型(Generic)允许在定义类、接口、方法时使用类型参数,使代码在编译时就进行类型检查,避免强制类型转换。

例子:

List<String> list = new ArrayList<>();
list.add("Java");
String s = list.get(0); // 不需要强制类型转换

没有泛型前:

List list = new ArrayList();
list.add("Java");
String s = (String) list.get(0); // 需要强转,易出错

9.2 泛型的好处

优势 说明
编译期类型检查 提前发现错误,避免 ClassCastException
消除强制类型转换 简化代码
提高代码复用性 写一次泛型类,可适用于多种类型

9.3 泛型使用位置

1)泛型类

public class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

使用:

Box<String> stringBox = new Box<>();
stringBox.set("Hello");
System.out.println(stringBox.get()); // Hello

2)泛型接口

public interface Converter<F, T> {
    T convert(F from);
}

实现类:

public class StringToIntegerConverter implements Converter<String, Integer> {
    public Integer convert(String from) {
        return Integer.parseInt(from);
    }
}

3)泛型方法

public class Util {
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }
}

调用:

String[] arr = {"A", "B", "C"};
Util.printArray(arr);

9.4 常见泛型写法

写法 说明
<T> 单个类型参数,常见
<K, V> 多个类型参数,如 Map
<E> 常用于集合(Element)

9.5 泛型通配符 ?

1)<?> 表示任意类型

public void printList(List<?> list) {
    for (Object o : list) {
        System.out.println(o);
    }
}

不能往 List<?> 添加任何元素(除了 null),因为编译器不知道真实类型。

2)<? extends T> 上界通配符:T 及其子类

public void printUpper(List<? extends Number> list) {
    // list 可能是 List<Integer>, List<Double>...
}

特点:可读不可写(写入时类型不明确)

3)<? super T> 下界通配符:T 及其父类

public void printLower(List<? super Integer> list) {
    list.add(10); // 可以添加 Integer 或其子类
}

特点:可写不可读(读取时只能当 Object)

9.6 泛型擦除(类型擦除)

Java 泛型只在编译期有效,编译后会擦除类型信息,称为 Type Erasure

List<String> list = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

System.out.println(list.getClass() == list2.getClass()); // true

9.7 泛型的限制

限制 说明
不能使用基本类型(int、double 等) 用包装类代替,如 Integer
泛型类中不能创建泛型数组 new T[] 不允许
不能在泛型中使用 instanceof 判断类型 instanceof T 不合法

9.8 类型变量命名规范

符号 含义
T Type 类型
E Element 元素(集合常用)
K Key 键(Map 常用)
V Value 值(Map 常用)
N Number 数值类型

9.9 示例:泛型与集合结合使用

Map<String, List<Integer>> map = new HashMap<>();
List<Integer> scores = new ArrayList<>();
scores.add(90);
map.put("Tom", scores);

System.out.println(map.get("Tom").get(0)); // 90

9.10 泛型 VS Object 的对比

特性 使用 Object 使用泛型
类型检查 运行时检查,风险高 编译时检查,安全
可读性 低,需要强转 高,清晰直观
类型转换 必须强转 自动完成

9.11 小结

概念 关键点
泛型类 <T> 定义类中的类型
泛型方法 <T> 返回类型 方法名(...)
通配符 <?>, <? extends T>, <? super T>
泛型擦除 编译后泛型信息被擦除
编译期安全性 最大优势是避免 ClassCastException

十、Arrays 工具类

10.1 Arrays 工具类简介

  • 包名java.util.Arrays

  • 作用:提供数组的排序、搜索、复制、填充、比较、转字符串等静态方法

  • 特点:全部是静态方法(static),不需要创建对象,直接用类名调用即可。

10.2 常用方法总览(分类汇总)

方法分类 常用方法 简介
排序 sort() 排序数组(默认升序)
查找 binarySearch() 使用二分查找定位元素(排序后使用)
比较 equals() 判断两个数组是否相等(元素逐一比较)
复制 copyOf(), copyOfRange() 拷贝数组
填充 fill() 用指定值填充整个数组
输出 toString() 转换为字符串(调试友好)
并行操作 parallelSort() 多线程快速排序(大数组更快)

10.3 各方法详细讲解

1)sort() – 数组排序

int[] arr = {3, 1, 5, 2};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 5]

支持对基本类型和对象数组排序。对象数组需实现 Comparable 或传入 Comparator

String[] names = {"Bob", "Alice", "Eve"};
Arrays.sort(names); // 按字母排序

2)binarySearch() – 二分查找(数组必须已排序)

int[] arr = {1, 2, 3, 5};
int index = Arrays.binarySearch(arr, 3); // 返回索引:2
int indexNotFound = Arrays.binarySearch(arr, 4); // 返回插入点位置的负值 -4(-插入位置 -1)

使用前必须排序,否则结果不可预测。

3)equals() – 比较两个数组是否完全相等

int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(Arrays.equals(a, b)); // true

对于二维数组(多维),需要用 deepEquals()

int[][] x = {{1, 2}, {3, 4}};
int[][] y = {{1, 2}, {3, 4}};
System.out.println(Arrays.deepEquals(x, y)); // true

4)copyOf()copyOfRange() – 数组复制

int[] arr = {1, 2, 3};
int[] copy = Arrays.copyOf(arr, 5); // 多余位置填 0: [1, 2, 3, 0, 0]

int[] part = Arrays.copyOfRange(arr, 1, 3); // 复制索引1到3(不含3):[2, 3]

5)fill() – 填充数组

int[] arr = new int[5];
Arrays.fill(arr, 7); // 所有元素变成 7: [7, 7, 7, 7, 7]

也可以指定范围:

Arrays.fill(arr, 1, 4, 99); // 填充索引 1~3(不含4):[7, 99, 99, 99, 7]

6)toString() – 数组转字符串

int[] arr = {1, 2, 3};
System.out.println(Arrays.toString(arr)); // [1, 2, 3]

二维数组使用 deepToString()

int[][] matrix = {{1,2}, {3,4}};
System.out.println(Arrays.deepToString(matrix)); // [[1, 2], [3, 4]]

7)asList() – 将数组转为 List(固定长度)

String[] array = {"A", "B", "C"};
List<String> list = Arrays.asList(array); // 支持列表操作

list.set(1, "Z"); // 修改元素
System.out.println(list); // [A, Z, C]

// list.add("D"); // 会抛异常:UnsupportedOperationException

返回的 List 是固定长度的,不支持 add/remove 操作。

8)parallelSort() – 并行排序(Java 8+)

int[] largeArray = new int[1000000];
// 填充数据...
Arrays.parallelSort(largeArray);

它使用多线程对大数组排序,速度比 sort() 更快(当数据量大时),但小数组效果不明显。

10.4 Arrays 工具类的使用建议

场景 建议方法
简单排序 Arrays.sort()
大数据排序(Java 8+) Arrays.parallelSort()
拷贝数组 Arrays.copyOf()
找元素(已排序) Arrays.binarySearch()
填充默认值 Arrays.fill()
调试输出数组 Arrays.toString()

10.5 与集合框架联动

可以通过 Arrays.asList() 将数组转为 List,适配 Java 集合操作:

String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
Collections.reverse(list); // 反转
System.out.println(list);  // [c, b, a]

10.6 常见注意事项

注意点 说明
asList() 返回的是固定长度 List 不能添加/删除元素
copyOf() 目标长度不够会截断,过长会补零
binarySearch() 必须在排序后使用
排序为原地排序(会修改原数组)

10.7 小结

方法 说明
sort() 升序排序
parallelSort() 多线程排序(Java 8+)
binarySearch() 二分查找(必须排序)
equals() 判断是否内容相等
copyOf() 拷贝数组
fill() 填充数组
toString() 转字符串
asList() 数组转 List
deepToString() 多维数组转字符串
deepEquals() 多维数组比较

十一、Collections 工具类

11.1 Collections 工具类简介

  • 包名java.util.Collections

  • 作用:提供对集合对象进行排序、查找、填充、线程安全化、反转、打乱顺序等操作的静态方法集合

  • Arrays 类似:它是用于集合的工具类,而 Arrays 是数组的工具类。

11.2 常用方法总览(分类整理)

功能分类 方法举例
排序 sort(List)reverseOrder()
查找 binarySearch(List, key)max()min()
修改 reverse()shuffle()fill()replaceAll()
同步 synchronizedList()synchronizedMap()
不可变集合 unmodifiableList()singletonList()
线程安全集合 synchronizedXxx()
其他 frequency()disjoint()

11.3 各方法详细讲解

1)排序:Collections.sort()

List 进行排序,要求元素实现了 Comparable 接口:

List<Integer> list = Arrays.asList(3, 1, 2);
Collections.sort(list);
System.out.println(list); // [1, 2, 3]

自定义排序规则(传入 Comparator):

Collections.sort(list, (a, b) -> b - a); // 降序
System.out.println(list); // [3, 2, 1]

2)查找:binarySearch()max()min()

List<String> names = Arrays.asList("Bob", "Alice", "Tom");
Collections.sort(names); // 先排序
int idx = Collections.binarySearch(names, "Tom");
System.out.println(idx); // 查找 Tom 的索引

System.out.println(Collections.max(names)); // Tom(字典序最大)
System.out.println(Collections.min(names)); // Alice(最小)

3)修改内容

reverse():反转列表顺序

Collections.reverse(list);

shuffle():打乱顺序(常用于洗牌)

Collections.shuffle(list);

fill():将所有元素替换为指定值

Collections.fill(list, 99); // 全部改为 99

replaceAll():将某值替换为另一个值

Collections.replaceAll(list, 99, 0); // 把所有99替换为0

4)线程安全包装:synchronizedXxx()

将集合包装为线程安全版本:

List<String> safeList = Collections.synchronizedList(new ArrayList<>());
Map<String, String> safeMap = Collections.synchronizedMap(new HashMap<>());

注意:虽然线程安全,但遍历时仍需要外部同步锁

synchronized (safeList) {
    for (String s : safeList) {
        // 遍历
    }
}

5)不可变集合(只读集合)

List<String> readonly = Collections.unmodifiableList(new ArrayList<>());
readonly.add("X"); // 会抛出 UnsupportedOperationException

可用于 API 返回值,防止外部修改。

6)单元素集合:singletonList()singletonMap()

List<String> onlyOne = Collections.singletonList("hello");
Map<String, String> map = Collections.singletonMap("key", "value");

生成只能包含一个元素的集合。

7)工具方法

frequency():统计某个元素出现的次数

List<String> list = Arrays.asList("a", "b", "a", "c");
int freq = Collections.frequency(list, "a"); // 2

disjoint():判断两个集合是否没有交集

boolean result = Collections.disjoint(list1, list2);

11.4 实战场景

场景 方法
快速排序 List Collections.sort()
洗牌算法 Collections.shuffle()
线程安全 List Collections.synchronizedList()
防止外部修改返回值 Collections.unmodifiableList()
查询最大/最小值 Collections.max() / min()
查找元素位置 Collections.binarySearch()(排序后)

11.5 注意事项

注意点 说明
binarySearch() 必须排序后使用 否则结果不准确
synchronizedXxx() 不等于绝对线程安全 遍历时仍需加锁
unmodifiableXxx() 只是包装,不可修改 调用 add() 会抛异常
singletonList() 集合固定长度不可扩展 不支持添加多个元素

11.6 与 Arrays 工具类的对比

工具类 用于对象 常用操作
Arrays 数组 排序、复制、比较、查找等
Collections 集合(List等) 排序、线程安全、不可变、打乱等

11.7 小结

方法名 功能简介
sort(List) 升序排序
reverse(List) 反转顺序
shuffle(List) 打乱顺序
binarySearch(List, x) 二分查找
max(List) / min() 最大 / 最小值
replaceAll() 替换所有匹配元素
frequency() 某元素出现次数
disjoint() 判断是否无交集
synchronizedList() 转线程安全 List
unmodifiableList() 转只读 List

十二、包装类

12.1 什么是包装类(Wrapper Class)

包装类是 Java 为 每个基本数据类型 提供的 类类型封装,用于将 基本类型转换为对象类型,以便在需要对象的场合中使用。

基本类型 包装类
byte Byte
short Short
int Integer 
long Long
float Float
double Double
char Character 
boolean Boolean 

12.2 为什么需要包装类

  • 集合不能直接存储基本类型
    Java 集合如 ArrayList<int> 是不允许的,但 ArrayList<Integer> 是可以的。

  • 对象方法调用需要对象
    基本类型没有方法,但包装类可以调用 equals()compareTo() 等方法。

  • 工具类支持
    如:Collections.max(List<Integer>) 只能处理对象。

  • 与泛型协同工作
    Java 泛型只支持对象类型。

12.3 如何使用包装类

创建包装类对象

Integer a = new Integer(10);      // 旧写法,不推荐
Integer b = Integer.valueOf(10);  // 推荐

Double d = Double.valueOf(3.14);
Character ch = Character.valueOf('A');

12.4 自动装箱 / 自动拆箱(Java 5 引入)

自动装箱(Auto Boxing)

将基本类型 自动转换为对应的包装类对象

int x = 5;
Integer obj = x; // 自动装箱:等同于 Integer.valueOf(x)

自动拆箱(Auto Unboxing)

将包装类对象 自动转换为基本类型

Integer obj = 10;
int y = obj; // 自动拆箱:等同于 obj.intValue()

12.5 常用方法(以 Integer 举例)

Integer x = Integer.valueOf("123");   // 字符串转 Integer
int y = x.intValue();                 // 拆箱为 int
String s = Integer.toString(456);     // int 转字符串
int z = Integer.parseInt("789");      // 字符串转 int

类似的方法也适用于 Double.parseDouble(), Boolean.parseBoolean() 等。

12.6 包装类中的常量与缓存

Integer 缓存机制

Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true 

Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false 

原因:

  • Java 缓存了 -128 到 127 的整数,所以这段范围内的包装类对象不会创建新对象,而是从缓存中取出同一个对象。

  • 超出范围就会创建新的对象。

12.7 包装类的不可变性(Immutable)

包装类对象是不可变的,一旦创建,值就不能更改。

Integer x = 10;
x = x + 1; // 实际上创建了一个新的 Integer 对象

12.8 包装类和 == / .equals() 的区别

Integer a = 128;
Integer b = 128;

System.out.println(a == b);      // false,比较的是引用
System.out.println(a.equals(b)); // true,比较的是值

结论:

  • 比较数值内容,使用 .equals()

  • == 比较的是对象引用地址,只有在 缓存区 -128~127 内才可能是 true

12.9 包装类使用场景总结

场景 示例
集合中使用基本类型 List<Integer>
解析字符串为数字 Integer.parseInt("12")
比较两个数字大小 Integer.compare(a, b)
与泛型协同使用 Map<String, Double>
与 Stream/Optional 协作 Optional<Integer>

12.10 八个包装类概览

包装类 常用静态方法 特点 / 注意事项
Integer valueOf(), parseInt() 有缓存区 -128~127 
Double valueOf(), parseDouble() 没有缓存
Boolean parseBoolean(), valueOf() Boolean.TRUE/FALSE 单例
Character isDigit(), isLetter() 方法用于判断字符类型
Long valueOf(), parseLong() 也有缓存(默认 -128~127)
Float valueOf(), parseFloat() 没有缓存
Short valueOf(), parseShort() 有缓存
Byte valueOf(), parseByte() 有缓存

12.11 与基本类型的区别对比

对比项 基本类型 int 包装类 Integer
是否是对象 是 
是否可为空 可以为 null 
占用内存 多(有对象开销)
支持泛型/集合 是 
默认值 0 null

十三、自动装箱与拆箱

13.1 什么是自动装箱与自动拆箱?

这是 Java 从 JDK 1.5(Java 5) 引入的特性,目的是让 基本数据类型与包装类之间的转换更自然、更简洁,从而方便与集合、泛型等机制协作。

名称 作用
自动装箱 基本类型 ➜ 包装类对象
自动拆箱 包装类对象 ➜ 基本类型

13.2 基本使用示例

自动装箱

int num = 10;
Integer obj = num;  // 自动装箱,相当于:Integer obj = Integer.valueOf(num);

自动拆箱

Integer obj = 20;
int num = obj;      // 自动拆箱,相当于:int num = obj.intValue();

13.3 装箱与拆箱的实际过程

操作代码 实际编译后的代码
Integer x = 1; Integer x = Integer.valueOf(1);
int y = x; int y = x.intValue();

自动装箱就是调用包装类的静态方法 valueOf()
自动拆箱就是调用包装类实例的方法 xxxValue()

13.4 自动装箱 & 拆箱在表达式中的表现

Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true(缓存范围内)

Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false(超出缓存)

System.out.println(c == 200); // true,c 自动拆箱为 int 后比较
  • c == 200:包装类 Integer 会拆箱为 int,与 int 进行数值比较。

13.5 自动拆箱带来的潜在问题(空指针)

注意!拆箱 null 会抛异常:

Integer obj = null;
int num = obj; // NullPointerException!

原因:尝试执行 obj.intValue(),而 objnull,所以抛出异常。

13.6 在集合与泛型中的应用

集合(如 List)不能存储基本类型,但可以通过自动装箱存储包装类对象:

List<Integer> list = new ArrayList<>();
list.add(1);       // 自动装箱为 Integer.valueOf(1)
int x = list.get(0); // 自动拆箱为 int

13.7 自动装箱缓存机制(Integer 缓存池)

Integer 缓存池范围:-128 ~ 127

Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true 

Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false 
  • Integer.valueOf(int i) 会使用缓存池:-128 ~ 127 范围内直接返回缓存对象。

  • 超出范围会创建新对象,因此不是同一个引用。

这也解释了为什么 == 比较时结果不一致。

13.8 自动装箱的性能问题

装箱会创建对象,在高频操作中会影响性能。

public static void main(String[] args) {
    long start = System.nanoTime();
    Long sum = 0L; // Long 类型:会频繁装箱拆箱

    for (long i = 0; i < 1_000_000; i++) {
        sum += i;
    }
    long end = System.nanoTime();
    System.out.println("耗时: " + (end - start) / 1e6 + " ms");
}

优化建议: 使用基本类型:

long sum = 0L; // 更快更轻

13.9 总结对比

特性 自动装箱 自动拆箱
转换方向 基本类型 ➜ 包装类对象 包装类对象 ➜ 基本类型
原理方法 valueOf() xxxValue()(如 intValue)
应用场景 集合、泛型、方法参数 运算表达式、返回值
可能问题 创建过多对象,影响性能 空对象拆箱抛出 NullPointerException
缓存机制 有(Integer 等)

网站公告

今日签到

点亮在社区的每一天
去签到