Java-集合与泛型:List、Set、Map及其实现类

 一、包装类(Wrapper Classes)

集合(如ArrayList)只能存储对象,不能存储基本类型(如int)。

包装类就是为每个基本类型提供的对应引用类型,实现“基本类型 <-> 对象”的转换。

基本类型 包装类
int Integer
double Double
char Character
boolean Boolean
  • 装箱(Boxing):将基本类型转换为包装类对象。例如:Integer i = Integer.valueOf(10);

  • 拆箱(Unboxing):将包装类对象转换为基本类型。例如:int num = i.intValue();

  • 自动装箱/拆箱(Auto-boxing/Unboxing):Java 5+ 开始支持直接赋值,编译器会自动转换。例如:Integer i = 10; 和 int num = i;

public class WrapperDemo {
    public static void main(String[] args) {
        // 1. 手动装箱和拆箱
        Integer numObj = Integer.valueOf(100); // 装箱
        int num = numObj.intValue();           // 拆箱

        // 2. 自动装箱和拆箱
        Integer autoBox = 200;    // 自动装箱:int -> Integer
        int autoUnbox = autoBox;  // 自动拆箱:Integer -> int

        // 3. 字符串与基本类型的转换
        // parseInt()将字符串解析为基本类型
        int parsedInt = Integer.parseInt("123"); // 字符串 -> int
        double parsedDouble = Double.parseDouble("3.14");
        // toString():将包装类对象转为字符串
        String str = Integer.toString(456);      // int -> 字符串

        // 4. 常量,MAX_VALUE获取该类型的最大值
        System.out.println("int最大值:" + Integer.MAX_VALUE);
    }
}

 二、集合框架(Collection Framework)

集合用于保存数量不确定或具有映射关系的对象,包括Collection 接口(单列集合)、Map 接口(双列集合,键值对);

主要用于替代传统的数组,解决数组长度固定、无法保存复杂映射关系等局限性。

Collection 接口(单列集合、每个元素是1个对象)

  • List:有序、可重复、有索引。

    • ArrayList:底层数组,查询快,增删慢(尾部增删除外)。

    • LinkedList:底层双向链表,增删快(特别是头部/中间),查询慢。

  • Set:无序、不可重复。

    • HashSet:底层HashMap,元素不重复,存入顺序与取出顺序不一定一致。

    • TreeSet:底层红黑树,元素自动排序

Map 接口(双列集合,键值对保存对象)

  • HashMap:底层数组+链表/红黑树(JDK 1.8+),键不重复;

    • 允许一个 null 键和多个 null 值

  • TreeMap:底层红黑树,键自动排序。

 

实现原理介绍:哈希表是一个封装好的工具,是实现键值对操作的核心。

  • java实现哈希表:数组 + 哈希算法 + 冲突解决机制(链表/红黑树)
  • python实现哈希表:数组+ 哈希算法+ 冲突解决机制(开放寻址)
  • 存储键值对
    • 把key传入哈希函数,得到哈希值
    • 把哈希值进行特定的计算,得到底层数组的下标
    • 定位到数组位置,遍历该位置数据,找有没有相等的 Key
      • 有相等的Key:覆盖旧Value
      • 没有相同的Key:创建一个新的节点,将节点放入该数组位置
    • 节点(Node/Entry):同时包含 Key 和 Value
  • 根据键获取值
    • 把key传入哈希函数,得到哈希值
    • 把哈希值进行特定的计算,得到底层数组的下标
    • 定位到数组位置后,进行 Key 的对比,确保找对
    • 获取数组位置对应的节点,再获取节点存放的Value

List 接口(最常用)

核心操作: 添加、获取、删除、修改、遍历。

ArrayList:适合多读、插入删除少的场景。

LinkedList:适合频繁进行头部/中间插入删除。

import java.util.ArrayList;
import java.util.Iterator;

public class ListDemo {
    public static void main(String[] args) {
        // 使用泛型指定只能存储 String 类型
        ArrayList<String> list = new ArrayList<>();

        // 1. 添加元素
        list.add("Java");
        list.add("Python");
        list.add(1, "C++"); // 在索引1处插入

        // 2. 获取和修改
        String lang = list.get(0);       // 获取索引0的元素
        list.set(2, "Go");               // 将索引2的元素改为"Go"

        // 3. 删除
        list.remove("Python");   // 删除指定元素(第一个匹配的)
        // list.remove(1);       // 删除索引1的元素

        // 4. 遍历方式(三种)
        // 方式一:for-each(最简洁,不能删除元素)
        for (String s : list) {
            System.out.println(s);
        }

        // 方式二:迭代器(可以安全删除)
        Iterator<String> it = list.iterator();
        // hasNext()判断是否有下一个元素
        while (it.hasNext()) {
            String s = it.next(); //next()获取下一个元素
            if (s.equals("Go")) {
                it.remove(); // 安全删除
            }
        }

        // 方式三:普通for(带索引,只适用于List)
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
 

Map 接口(键值对)

核心操作: 存(put)、取(get)、删(remove)、遍历键/值/键值对。

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapDemo {
    public static void main(String[] args) {
        HashMap<String, Integer> scores = new HashMap<>();

        // 1. 添加键值对
        scores.put("Alice", 90);
        scores.put("Bob", 85);
        scores.put("Alice", 95); // 键相同,新值覆盖旧值

        // 2. 获取值
        int aliceScore = scores.get("Alice"); // 95
        Integer bobScore = scores.get("Bob"); // 85

        // 3. 检查键是否存在
        if (scores.containsKey("Alice")) {
            System.out.println("Alice exists");
        }

        // 4. 遍历方式
        // 遍历所有键
        for (String key : scores.keySet()) {
            System.out.println(key + " -> " + scores.get(key));
        }
        // 遍历所有键值对(推荐)
        for (Map.Entry<String, Integer> entry : scores.entrySet()) {
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }
    }
}

 

Set 接口(保证元素唯一)

核心: 存入HashSet的元素必须正确重写hashCode()equals()方法,

HashSet 先比较 hashCode(),再比较 equals()。只有两者都相同才认为是重复元素

import java.util.HashSet;

public class SetDemo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("apple");
        set.add("banana");
        set.add("apple"); // 重复元素,不会被加入

        System.out.println(set.size()); // 输出 2
        for (String s : set) {
            System.out.println(s); // 输出顺序可能是 banana, apple(无序)
        }
    }
}
 

三、泛型(Generic)

泛型用来限制类、接口、方法中能够操作的数据类型(保证类型转换的绝对安全)

  • 类型擦除:泛型信息在编译后会被移除,运行时 List<String> 和 List<Integer> 是同一类型。

T、定义时用的类型参数:

  • 泛型类:class Box<T> { private T item; ... }
  • 泛型方法:public <T> void print(T t) { ... }

?、通配符、使用时用的未知类型:

  • <?>:未知类型(只能读,不能写),不关心具体类型
  • 上界通配符:<? extends T>:T或T的子类,能读为T,但不确定具体子类型,所以不能写。
    • 应用场景:取数据场景(比如遍历、复制)
  • 下界通配符:<? super T>:T或T的父类。能写T及其子类,但不确定具体父类型,所以只能读为Object。
    • 应用场景:存数据场景(比如添加、填充)。
无边界通配符
   // 无边界通配符:可以接收任何类型的 List
    public static void printList(List<?> list) {
        // 1. 可以读取,但只能赋值给 Object
        for (Object obj : list) {
            System.out.print(obj + " ");
        }
        System.out.println();

        // 2. 不能写入(除 null 外)
        // list.add("hello");      // 编译错误
        // list.add(123);          // 编译错误
        // list.add(new Object()); // 编译错误
        list.add(null); // 只能添加 null
    }

List<?>:未知类型

1、不能写: 如果是List<Dog>,添加Cat对象是非法的

2、可以读:  只能赋值给 Object

                                

上界通配符
    // 上界通配符:可以接收 Animal或其子类的 List
    public static void print(List<? extends Animal> list) {
        // 1. 可以读:读取的元素可以赋值给 Animal
        for (Animal animal : list) {
            System.out.println(animal.getName());
        }

        // 2. 不能写:不能添加元素(null 除外)
        // list.add(new Cat("小白")); // 编译错误!
        // list.add(new Animal("动物")); // 编译错误!
        list.add(null); // 只能添加 null
    }

List<? extends Animal>:Animal的某个未知子类型的列表

可能类型: List<Animal>List<Cat>、List<Dog>

1、不能写:如果是 List<Dog>,添加Cat对象是非法的

---编译器为了绝对的类型安全,必须假设最坏的情况

2、可以读:取出的元素都一定是 Animal 或其子类的对象

Animal a = list.get(0); // 完全安全!
下界通配符
// 下界通配符:可以接收 Cat 或其父类的 List
    public static void addCat(List<? super Cat> list) {
        // 1. 可以写:添加 Cat 或其子类对象
        list.add(new Cat("小白"));
        // list.add(new Dog("小黑"));
//编译错误!Dog 不是 Cat 或其子类 // 2. 不能读:只能读取为 Object Object obj = list.get(0); // Cat cat = list.get(0); // 编译错误!不能直接赋值给 Cat System.out.println("添加成功"); }

List<? super Cat>:Cat的某个未知父类型列表

可能类型:  List<Cat>List<Animal>、 List<Object>

1、可以写:添加任意的子类型都能向上转型为对应父类型,

2、不能读:编译器无法确定读取到的具体类型,只能是Object

posted @ 2026-08-07 10:25  Fēngwèi  阅读(4)  评论(0)    收藏  举报