Java 集合与泛型完整讲解
一、整体关系
泛型是 Java 集合体系的语法基础:集合本质是存储一组对象的容器,早期Object类型集合存在类型强制转换、类型安全隐患;泛型(JDK5引入) 给集合约束元素类型,实现编译期类型检查、消除强制类型转换、提升代码复用性。
二、Java 集合体系(核心架构)
集合分为两大派系:Collection(单列集合,存单个元素)、Map(双列集合,键值对 key-value),全部位于java.util包。
- Collection 单列集合根接口
Collection
├─ List(有序可重复,索引访问)
│ ├─ ArrayList:数组实现,查询快、增删尾部快,线程不安全
│ ├─ LinkedList:双向链表,头尾增删快,随机查询慢
│ └─ Vector:线程安全数组,效率低(过时)
├─ Set(无序不可重复)
│ ├─ HashSet:哈希表实现,无序,基于 hashCode+equals 去重
│ ├─ LinkedHashSet:哈希+链表,保留插入顺序
│ └─ TreeSet:红黑树,元素可排序(实现Comparable/传Comparator)
└─ Queue(队列,先进先出)
├─ ArrayDeque:数组双端队列
└─ PriorityQueue:优先级队列(堆结构) - Map 双列集合根接口(不继承Collection)
Map
├─ HashMap:哈希表,键无序,JDK8 数组+链表+红黑树,线程不安全
├─ LinkedHashMap:保留键插入顺序
├─ TreeMap:红黑树,按键排序
├─ Hashtable:古老线程安全Map,效率低(过时)
└─ Properties:键值都是String,读写配置文件
三、为什么需要泛型?(无泛型的痛点)
无泛型(JDK5之前)
集合默认存储Object,任意对象都能存入,取出必须强转,极易出现ClassCastException运行时异常:
List list = new ArrayList();
list.add("abc");
list.add(123); // 混入Integer
String s = (String) list.get(1); // 运行时报类型转换异常
引入泛型后
编译阶段强制校验存入元素类型,取出无需强转,把运行时错误提前到编译期:
Listlist = new ArrayList<>(); // 泛型约束只能存String
list.add("abc");
// list.add(123); 编译直接报错,杜绝类型错误
String s = list.get(0); // 自动类型推导,无需强转
四、泛型核心语法 - 泛型类
在类名后定义(T为类型形参,任意大写字母:T/E/K/V常用),约束类中成员、方法的类型:
// 自定义泛型容器类
public class MyContainer{
private T data;
public void setData(T data) { this.data = data; }
public T getData() { return data; }
}
// 使用:指定实际类型String
MyContainercontainer = new MyContainer<>();
container.setData("hello"); - 泛型方法
方法上单独声明泛型,优先级高于类泛型,可用于静态方法(类泛型静态方法不能使用):
//声明泛型,返回List
public staticList copyList(List src) {
Listdest = new ArrayList<>();
dest.addAll(src);
return dest;
} - 泛型接口
// 泛型接口
public interface IStore{
void save(T t);
}
// 实现类指定具体类型
class StringStore implements IStore{
@Override
public void save(String s) {}
}
五、泛型关键特性(集合高频使用) - 类型擦除(本质原理)
泛型只存在编译期,编译后全部擦除为原生类型(Raw Type):
• List、List 编译后都变成List(原生集合)
• 字节码层面只保留Object,JVM 不区分泛型类型
• 因此不能用泛型类型创建数组、不能用instanceof判断泛型
// 错误:类型擦除后无法区分
// List[] arr = new List [5]; - 通配符 ?(适配任意泛型集合)
用于方法参数接收任意泛型类型的集合,不能添加元素(类型不确定),只能读取:
// 可以接收 List、List 等所有List
public void printAll(List<?> list) {
for (Object o : list) {
System.out.println(o);
}
// list.add(1); 编译报错,无法确定元素类型
}
(1)上界通配符 ? extends T
只能读取,不能写入,代表T及其子类,常用于读取集合数据:
// 只能存Number、Integer、Double等Number子类
public void showNum(List<? extends Number> list) {
Number num = list.get(0); // 读取没问题
// list.add(10); 报错,不知道具体子类类型
}
(2)下界通配符 ? super T
只能写入T及子类,读取只能用Object接收,常用于写入集合:
// 只能存Integer、Integer父类Number/Object
public void addInt(List<? super Integer> list) {
list.add(100); // 可写入Integer
Object o = list.get(0); // 读取只能转Object
} - 泛型无继承关系
List不是 List

浙公网安备 33010602011771号