java-集合

使用 List

一、List 基础概念

  1. 定义

    • List 是 Java 集合框架中的有序列表,元素按插入顺序存储,支持通过索引(从 0 开始)访问。
    • 类似数组,但提供动态增删功能,避免数组手动扩容和元素移动的繁琐。
  2. 核心接口方法

    • 添加:add(E e)(末尾)、add(int index, E e)(指定位置)
    • 删除:remove(int index)(按索引)、remove(Object e)(按元素)
    • 访问:get(int index)(获取元素)、size()(获取大小)

二、List 实现类对比

特性 ArrayList LinkedList
数据结构 动态数组(基于数组实现) 双向链表
随机访问 高效(O (1)) 低效(需从头遍历,O (n))
中间增删 需移动元素(O (n)) 仅修改指针(O (1))
内存占用 较小(连续存储) 较大(节点包含前后指针)
推荐场景 频繁随机访问、尾部增删 频繁中间增删

最佳实践 :优先使用ArrayList,仅在需要高效中间增删时用LinkedList

三、List 特点

  1. 元素允许性

    • 支持重复元素和null值(如list.add(null)合法)。
  2. 有序性

    • 元素顺序由插入顺序决定,索引稳定。

四、创建 List 的方式

  1. 常规创建

    • List<String> list = new ArrayList<>();(动态数组)
    • List<String> list = new LinkedList<>();(链表)
  2. 快速创建只读 List(JDK 9+)

    • List<Integer> list = List.of(1, 2, 3);(不可变,不允许null,否则抛异常)
  3. 数组转 List

    • List<Integer> list = Arrays.asList(array);(JDK 11 前)或List.of(array)(JDK 11+)。

五、遍历 List 的方法

  1. 索引遍历(不推荐 LinkedList)

    java 复制代码
    for (int i = 0; i < list.size(); i++) { String element = list.get(i); }
  2. 迭代器遍历(推荐)

    java 复制代码
    for (Iterator<String> it = list.iterator(); it.hasNext(); ) { String element = it.next(); }
  3. 增强 for 循环(语法糖,内部用 Iterator)

    java 复制代码
    for (String element : list) { /* 遍历元素 */ }

六、List 与数组的转换

  1. List 转数组

    • 无类型参数:Object[] array = list.toArray();(丢失类型信息)
    • 带类型参数:String[] array = list.toArray(new String[0]);(推荐,自动匹配类型)
    • 函数式写法(JDK 16+):String[] array = list.toArray(String[]::new);
  2. 数组转 List

    • List<String> list = List.of(array);(只读)或new ArrayList<>(Arrays.asList(array));(可修改)

编写 equals 方法

一、List 集合与 equals 方法的关系

  1. List 的元素判断机制

    • Listcontains(Object o)indexOf(Object o)方法通过equals()而非==判断元素是否相等。
    • 示例:即使new String("C")与列表中的"C"是不同实例,仍能正确匹配,因为String覆写了equals()
  2. 自定义类的问题

    • 若自定义类(如Person)未覆写equals(),使用contains()等方法时会因引用不同而返回错误结果(即使内容相同)。

二、equals 方法的核心规则(Java 规范)

  1. 必须满足的 5 大条件

    • 自反性x.equals(x)恒为truex≠null)。
    • 对称性 :若x.equals(y)true,则y.equals(x)必为true
    • 传递性 :若x.equals(y)y.equals(z)true,则x.equals(z)必为true
    • 一致性 :只要对象状态不变,equals()结果恒定。
    • 非空性x.equals(null)恒为false

三、编写 equals 方法的步骤

  1. 定义 "相等" 的逻辑

    • 确定哪些字段相等时,对象即视为相等(如Personnameage)。
  2. 类型检查

    • instanceof判断待比较对象是否为当前类型,若是则转型后比较字段,否则返回false
  3. 字段比较

    • 引用类型字段 :使用Objects.equals(a, b)(自动处理null情况,两者为null时视为相等)。
    • 基本类型字段 :直接用==比较(如int age)。
    • 示例代码:
    java 复制代码
    public boolean equals(Object o) {
        if (o instanceof Person p) {
            return Objects.equals(this.name, p.name) && this.age == p.age;
        }
        return false;
    }

四、关键工具与最佳实践

  1. 简化引用类型比较

    • 使用java.util.Objects.equals()避免手动处理null,提升代码简洁性。
  2. 适用场景

    • 仅当需要在List等集合中使用contains()indexOf()时,才需覆写equals();否则可不实现。

Java 中 Map 的使用讲解

一、核心概念

  1. Map 数据结构

    • 一种键值(key-value)映射表,通过key快速查找value,比List遍历查找效率更高。
    • 接口定义为Map<K, V>,常用实现类为HashMap
  2. 基本操作

    • 存储put(K key, V value),若key已存在,旧value会被覆盖,返回旧值;否则返回null
    • 查询get(K key),若key不存在,返回nullcontainsKey(K key)用于判断key是否存在。

二、核心特性

  1. 键值规则

    • key不可重复,重复key会覆盖原有映射的value
    • value可重复,不同key可映射到相同value
  2. 无序性

    • Map不保证遍历顺序,与插入顺序或排序无关,不同 JDK 版本遍历顺序可能不同,依赖顺序的逻辑不可靠。

三、遍历方法

  1. 遍历 key

    • 通过keySet()获取所有keySet集合,使用for each循环:
    java 复制代码
    for (String key : map.keySet()) { ... }
  2. 遍历 key-value

    • 通过entrySet()获取所有键值对Map.Entry集合,直接获取keyvalue
    java 复制代码
    for (Map.Entry<String, Integer> entry : map.entrySet()) {
        String key = entry.getKey();
        Integer value = entry.getValue();
    }

四、注意事项

  • 效率优势Map通过哈希算法实现快速查找,平均时间复杂度为 O (1),远优于List的 O (n) 遍历。
  • null 值处理get()方法返回null表示key不存在,需注意空指针异常(NPE)。

编写 hashCode 方法

一、核心概念:HashMap 与 hashCode 的关系

  1. HashMap 的工作原理

    • 通过 空间换时间 ,使用数组存储值,根据键的 hashCode() 计算索引直接定位值的存储位置,无需遍历,提升查询效率。
    • 示例:键 "a" 计算索引为 1,对应值存储在数组索引 1 处。
  2. 键对象的核心要求

    • 作为 Map 的键,必须正确覆写 equals()hashCode() 方法:

      • equals() 用于判断键是否相等(内容相同而非同一对象)。
      • hashCode() 用于计算存储索引,确保相等的键(equals() 返回 true)生成相同的哈希值。

二、hashCode 方法的实现规范

  1. 必须满足的规则

    • 规则 1 :若两个对象相等(a.equals(b) == true),则它们的 hashCode() 必须相等。
    • 规则 2 :若两个对象不相等(a.equals(b) == false),它们的 hashCode() 应尽量不同(减少哈希冲突,提升效率)。
  2. 实现方法

    • 基础实现 :对每个参与 equals() 比较的字段计算哈希值,常用 31 * h + 字段哈希值 的形式(如字符串、基本类型)。

      java 复制代码
      @Override
      public int hashCode() {
          int h = 0;
          h = 31 * h + firstName.hashCode();
          h = 31 * h + lastName.hashCode();
          h = 31 * h + age;
          return h;
      }
    • 简化方案 :使用 Objects.hash(字段1, 字段2, ...) 自动处理 null 值,避免 NullPointerException

      java 复制代码
      return Objects.hash(firstName, lastName, age);
  3. 关键原则

    • equals() 中使用的字段必须全部包含在 hashCode() 的计算中,且不能包含未在 equals() 中使用的字段。

三、HashMap 深度解析(扩展知识)

  1. 哈希冲突与解决

    • 不同键可能生成相同的 hashCode(哈希冲突),HashMap 内部通过 链表(或红黑树,JDK 8+) 存储冲突的键值对,冲突越多,查询效率越低(需遍历链表)。
  2. 数组扩容机制

    • 初始默认容量为 16,通过 hashCode() & (容量 - 1) 计算索引(如容量 16 时,索引范围 0~15)。
    • 当元素数量超过阈值(默认容量 × 负载因子 0.75)时,数组扩容为原来的 2 倍(如 16→32),并重新计算所有键的索引,频繁扩容影响性能。
    • 优化 :提前指定容量(如 new HashMap<>(10000)),避免多次扩容(实际容量为大于指定值的最小 2 的幂,如 10000→16384)。

四、最佳实践与注意事项

  1. 必须同时覆写 equals 和 hashCode

    • 若覆写 equals() 而未覆写 hashCode(),会导致 HashMap 等集合类无法正确工作(如相等对象被视为不同键)。
  2. 提升哈希值质量

    • 合理选择参与计算的字段,减少哈希冲突,确保哈希值均匀分布。
  3. 值对象无特殊要求

    • HashMap 对存储的 value 对象无 equals()hashCode() 覆写要求,仅针对 key 对象。

使用 EnumMap

一、核心定位与优势

  1. 专属场景

    • Map的键(Key)为 ** 枚举类型(enum)** 时,EnumMap是官方推荐的最优实现,专为枚举键设计。
  2. 核心优势

    • 高效性 :无需计算键的hashCode(),直接通过枚举常量的ordinal()(自然顺序)定位内部数组索引,所有操作(增、删、查)均为 O (1) 时间复杂度。
    • 空间紧凑 :内部以数组存储值,无哈希表额外开销(如链表、红黑树),内存占用仅为存储值的必要空间,比HashMap更节省内存。
    • 类型安全 :键必须为枚举实例,编译期校验合法性,禁止null键(枚举常量本身不为null),值可接受null

二、基础用法与示例

  1. 创建与初始化

    java 复制代码
    import java.time.DayOfWeek;  
    import java.util.Map;  
    import java.util.EnumMap;  
    
    public class Main {  
        public static void main(String[] args) {  
            // 创建EnumMap,指定键的枚举类型为DayOfWeek  
            Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);  
            // 填充键值对(键必须为枚举常量)  
            map.put(DayOfWeek.MONDAY, "星期一");  
            // 遍历(按枚举自然顺序,即ordinal()顺序)  
            for (Map.Entry<DayOfWeek, String> entry : map.entrySet()) {  
                System.out.println(entry.getKey() + " → " + entry.getValue());  
            }  
        }  
    }  
  2. 接口化编程

    • 推荐通过Map接口引用EnumMap,遵循 "面向接口编程" 原则,支持与HashMap等实现无缝替换:
    java 复制代码
    Map<EnumType, Value> map = new EnumMap<>(EnumType.class); // 推荐  
    // 避免紧耦合实现类  
    // EnumMap<EnumType, Value> map = new EnumMap<>(EnumType.class);  

三、最佳实践与注意事项

  1. 适用场景

    • 枚举键映射:状态枚举(如订单状态)、类型转换(如枚举到描述 / 配置的映射)、高频访问的枚举键数据。
    • 性能敏感场景:需极致优化查找、插入效率时(如核心业务逻辑中的枚举键数据存储)。
  2. 对比选择

    实现类 优势 劣势 适用场景
    EnumMap 枚举键专用,O (1) 效率,空间紧凑 仅支持枚举键 键为枚举类型时
    HashMap 通用键,平均 O (1) 效率 需计算哈希,可能有哈希冲突 键为非枚举类型时
    TreeMap 键有序(自然顺序或定制顺序) O (logn) 效率,红黑树开销 需按键排序时
  3. 注意事项

    • 枚举顺序依赖 :内部数组索引由ordinal()决定,枚举常量顺序修改会影响存储顺序,建议枚举定义后避免调整顺序。
    • 键合法性 :禁止插入null键(会抛出NullPointerException),值可为null(与普通Map一致)。

四、原理与设计思想

  1. 底层实现

    • 基于数组存储,数组长度等于枚举常量的数量,索引直接对应枚举的ordinal()值。例如,DayOfWeek.MONDAYordinal()=0)对应数组索引 0,TUESDAYordinal()=1)对应索引 1,以此类推。
    • 无需哈希计算和冲突处理,直接通过枚举顺序定位,消除了HashMap的哈希碰撞和扩容开销。
  2. 设计原则

    • 克制设计:仅针对枚举键场景优化,牺牲通用性换取极致性能与空间效率。
    • 接口兼容 :完全实现Map接口,支持所有Map操作(如keySet()values()entrySet()),行为与普通Map一致。

使用 TreeMap

一、TreeMap 核心特性

  1. 有序映射表

    • 实现SortedMap接口,内部通过红黑树对 Key 进行排序,遍历时按 Key 顺序输出(非插入顺序)。
    • HashMap的区别 :Key 有序(自然排序 / 自定义排序),插入、查询时间复杂度为O(log n)(HashMap 平均O(1))。

二、Key的排序规则

  1. 自然排序(默认)
  • 要求 :Key必须实现Comparable接口(如StringInteger等内置类型已实现)。
  • 排序依据 :按compareTo()方法定义的顺序排序(如String字典序、Integer数值大小)。
  1. 自定义排序(Comparator
  • 适用场景 :Key未实现Comparable时,需在创建TreeMap时传入Comparator
  • compare(a, b)规则
    • a < b → 返回负数(如-1);
    • a == b必须返回0(否则导致查找异常);
    • a > b → 返回正数(如1)。

三、典型示例与注意事项

  1. 自然排序示例
java 复制代码
Map<String, Integer> map = new TreeMap<>();
map.put("orange", 1);
map.put("apple", 2);
map.put("pear", 3);
// 遍历Key顺序:apple(A)→ orange(O)→ pear(P)(String字典序)
  1. 自定义排序示例(按Person.name排序)
java 复制代码
Map<Person, Integer> map = new TreeMap<>(
    (p1, p2) -> p1.name.compareTo(p2.name)
);
  1. 错误示例与修正(未处理相等情况)

    • 错误代码(导致查找失败):
    java 复制代码
    // 未处理score相等时返回0,导致TreeMap认为不同Key相等
    return p1.score > p2.score ? -1 : 1; 
    • 正确代码(显式处理相等逻辑):
    java 复制代码
    return p1.score == p2.score ? 0 : (p1.score > p2.score ? -1 : 1);
    // 或使用Integer.compare(p1.score, p2.score)自动处理
  2. Key 的特殊性

    • 不依赖equals()hashCode() :TreeMap 通过比较逻辑(Comparable/Comparator)判断 Key 是否相等(返回0时视为相等,后续 Key 会覆盖前一个)。

四、核心注意事项

  1. 比较逻辑的严谨性

    • 必须严格遵循compare()规范,尤其是相等时返回0,否则会导致排序异常或查找失败(如示例中Student类因未处理相等情况导致get()返回null)。
  2. 适用场景

    • 推荐场景:需要按 Key 顺序遍历(如统计、排序展示)。
    • 性能 :红黑树实现,插入、删除、查询时间复杂度均为O(log n),适合中等规模数据。

使用 Set

一、Set 的核心概念

  1. 定义

    • Set 是 Java 集合框架中的接口,用于存储不重复元素 的集合,相当于仅存储Map的 key(value 为固定占位符)。

    • 核心方法:

      • add(E e):添加元素(成功返回true,重复则返回false)。
      • remove(Object e):删除元素。
      • contains(Object e):判断是否包含元素。
      • size():获取元素数量。
  2. 与 Map 的关系

    • HashSet 内部基于HashMap实现,每个元素作为HashMap的 key,value 为固定对象PRESENT

二、主要实现类

实现类 有序性 数据结构 元素要求 特点
HashSet 无序 哈希表(HashMap) 正确实现equals()hashCode() 查找效率高,遍历时顺序不确定(非插入顺序,非排序顺序)。
TreeSet 有序 红黑树(TreeMap) 实现Comparable接口或传入Comparator 遍历时按元素自然顺序(如 String 字典序)或自定义排序,适合需要有序存储的场景。

三、核心特性与使用场景

  1. 去重功能

    • 直接利用Set的不重复性去除重复元素,例如从List中去重:

      java 复制代码
      List<String> list = new ArrayList<>(Arrays.asList("a", "b", "a"));
      Set<String> set = new HashSet<>(list); // 自动去重
  2. 元素要求

    • HashSet / 普通 Set :元素需正确重写equals()hashCode(),否则无法正确判断重复。
    • TreeSet :元素需实现Comparable接口(自然排序),或在创建时传入Comparator(自定义排序)。
  3. 遍历顺序

    • HashSet:无序,遍历顺序不可预测。
    • TreeSet:有序,按自然顺序或Comparator定义的顺序遍历。

四、示例代码

  1. HashSet 基本操作

    csharp 复制代码
    Set<String> set = new HashSet<>();
    set.add("apple");
    set.add("banana");
    System.out.println(set.contains("apple")); // true
    set.remove("banana");
  2. TreeSet 有序存储

    csharp 复制代码
    Set<String> sortedSet = new TreeSet<>(); // 自然排序(字典序)
    sortedSet.add("z");
    sortedSet.add("a");
    for (String s : sortedSet) { // 输出:a, z
        System.out.println(s);
    }

五、练习:消息去重(按 sequence)

  • 需求 :接收重复消息,按sequence字段去重,保留首次出现的消息。

  • 解决方案

    1. 方法 1:利用 HashSet 存储 sequence

      java 复制代码
      Set<Integer> seen = new HashSet<>();
      List<Message> unique = new ArrayList<>();
      for (Message msg : received) {
          if (seen.add(msg.sequence)) { // 仅添加新sequence的消息
              unique.add(msg);
          }
      }
    2. 方法 2:自定义 Message 的 equals/hashCode

      重写equals()(比较 sequence)和hashCode(),直接将 Message 对象存入 HashSet:

      java 复制代码
      @Override
      public boolean equals(Object o) {
          if (o instanceof Message m) {
              return sequence == m.sequence;
          }
          return false;
      }
      @Override
      public int hashCode() {
          return Integer.hashCode(sequence);
      }
      Set<Message> set = new HashSet<>(received); // 自动去重
    3. 方法 3:TreeSet 排序去重

      传入Comparator按 sequence 排序并去重:

      java 复制代码
      Set<Message> set = new TreeSet<>(Comparator.comparingInt(m -> m.sequence));
      set.addAll(received); // 自动去重并排序

Java 中 Queue 的使用

一、Queue 基础概念

  1. 定义

    • 一种先进先出(FIFO)的有序集合,仅允许在队尾添加元素队首取出元素
    • List的区别:List支持任意位置操作,而Queue限定操作位置(队尾 / 队首)。
  2. 核心特性

    • 部分实现类有容量限制,部分无(如LinkedList无界)。
    • 避免向队列中添加null,否则难以区分 "队列为空" 和 "取出null元素"。

二、Queue 核心方法

操作类型 抛异常(失败时) 返回特殊值(失败时) 说明
添加元素 add(E e) offer(E e) 向队尾添加元素;add在队列满时抛IllegalStateExceptionoffer返回false
获取并删除队首 remove() poll() 取出队首元素并删除;队列为空时,removeNoSuchElementExceptionpoll返回null
获取队首元素 element() peek() 取出队首元素但不删除;队列为空时,elementNoSuchElementExceptionpeek返回null

三、方法对比与示例

  1. 添加元素

    • add():适合明确队列容量足够时使用,否则需捕获异常。
    java 复制代码
    try {
        q.add("Apple"); // 失败时抛异常
    } catch (IllegalStateException e) {
        // 处理异常
    }
    • offer():适合容量不确定时,通过返回值判断是否添加成功。
    java 复制代码
    if (q.offer("Apple")) {
        // 添加成功
    } else {
        // 添加失败
    }
  2. 获取并删除队首(poll() vs remove()

    • poll():安全获取元素,避免异常,常用于循环处理队列元素。
    java 复制代码
    while ((element = q.poll()) != null) {
        // 处理元素
    }
    • remove():强制删除,需确保队列非空。
  3. 获取队首但不删除(peek() vs element()

    • peek():查看队首元素(不改变队列状态),队列空时返回null
    • element():功能同peek(),但队列空时抛异常。

四、实现类与最佳实践

  1. 典型实现类

    • LinkedList:实现Queue接口,无界队列,适合通用场景。
    java 复制代码
    Queue<String> queue = new LinkedList<>(); // 面向接口编程
    • 其他实现:PriorityQueue(优先队列)、ArrayBlockingQueue(有界阻塞队列)等。
  2. 编程原则

    • 面向抽象编程 :通过Queue接口引用实现类(如LinkedList),提高代码灵活性。
    • 避免null元素 :防止poll()返回null时无法区分 "队列为空" 或 "有效null元素"。

使用 PriorityQueue

一、核心概念

  1. 优先队列(PriorityQueue)

    • 与普通Queue(FIFO)不同,PriorityQueue的出队顺序由元素的优先级 决定,调用remove()poll()时返回优先级最高的元素
    • 应用场景:如 VIP 插队场景,优先级高的元素(如 VIP 客户)可优先出队。

二、基本用法

  1. 默认排序(自然顺序)

    • 元素需实现Comparable接口,PriorityQueue按自然顺序排序。
    • 示例
    java 复制代码
    Queue<String> q = new PriorityQueue<>();
    q.offer("apple"); q.offer("pear"); q.offer("banana");
    // 输出顺序:apple(优先级最高)→ banana → pear(按字符串字典序)
    System.out.println(q.poll()); // apple
  2. 自定义排序(通过 Comparator)

    • 若元素未实现Comparable,可通过Comparator指定排序规则。
    • 示例(银行 VIP 优先级)
    java 复制代码
    Queue<User> q = new PriorityQueue<>(new UserComparator());
    // VIP(V开头)优先级高于普通客户(A开头),同类型按号码顺序
    class UserComparator implements Comparator<User> {
        public int compare(User u1, User u2) {
            char c1 = u1.number.charAt(0), c2 = u2.number.charAt(0);
            if (c1 != c2) return c1 == 'V' ? -1 : 1; // V开头优先级高
            return u1.number.compareTo(u2.number); // 同类型按号码排序
        }
    }

三、关键特性

  1. 元素要求

    • 要么实现Comparable接口(默认排序),要么提供Comparator(自定义排序)。
  2. 常用方法

    • offer():添加元素;poll():取出并移除优先级最高的元素(队列为空时返回null);peek():获取但不移除队首元素。
  3. 注意事项

    • 自定义Comparator需确保逻辑正确性,例如避免号码如A10排在A2前的错误(需按数值而非字典序比较)。

四、小结

  • 核心区别PriorityQueue按优先级出队,而非严格 FIFO。
  • 排序方式 :支持Comparable自然排序或Comparator自定义排序。
  • 适用场景:需要根据优先级动态处理元素的场景,如任务调度、实时数据处理等。

Java 中 Deque 接口的使用

一、Deque 基础概念

  1. 定义

    • 双端队列(Double Ended Queue),允许从队列两端(队首、队尾)进行元素的添加和删除操作。
    • 接口扩展自Queue,是 Java 集合框架的一部分。
  2. 核心功能

    • 元素添加:可从队首或队尾插入元素。
    • 元素获取:可从队首或队尾获取并删除元素,或仅查看元素(不删除)。

二、与 Queue 的方法对比

操作类型 Queue(单端队列) Deque(双端队列)
添加到队尾 add(E e) / offer(E e) addLast(E e) / offerLast(E e)(等效于 Queue 方法)
添加到队首 addFirst(E e) / offerFirst(E e)
获取并删除队首 remove() / poll() removeFirst() / pollFirst()
获取并删除队尾 removeLast() / pollLast()
查看队首(不删除) element() / peek() getFirst() / peekFirst()(等效于 Queue 方法)
查看队尾(不删除) getLast() / peekLast()

三、常用方法与最佳实践

  1. 推荐方法

    • 添加元素 :明确使用offerFirst()(队首)或offerLast()(队尾),避免直接调用offer()(默认队尾,易混淆)。
    • 删除元素pollFirst()(队首删除)、pollLast()(队尾删除),返回null而非抛出异常(安全)。
    • 查看元素peekFirst()(队首)、peekLast()(队尾),安全获取元素(不删除,返回null而非抛出异常)。
  2. 实现类

    • ArrayDeque:基于数组实现,性能高效,不支持null元素,适合频繁两端操作。
    • LinkedList:基于链表实现,同时实现ListQueueDeque接口,支持null元素,但性能略逊于ArrayDeque
  3. 编码规范

    • 面向接口编程 :通过Deque接口引用实现类(如Deque<String> deque = new LinkedList<>();),而非直接使用LinkedList,提升抽象层次。
    • 避免null元素 :多数实现类(如ArrayDeque)不支持null,添加null可能导致异常。

四、示例代码

java 复制代码
import java.util.Deque;
import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        Deque<String> deque = new LinkedList<>();
        // 添加元素(队尾、队首)
        deque.offerLast("A");  // 队尾添加"A"
        deque.offerFirst("C"); // 队首添加"C",当前队列:C -> A
        // 删除元素(队首、队尾)
        System.out.println(deque.pollFirst()); // 输出"C",剩余:A
        System.out.println(deque.pollLast());  // 输出"A",剩余:空
        // 安全获取元素(不删除)
        System.out.println(deque.peekFirst()); // 输出null(队列空)
    }
}

使用 Stack

一、Stack 基础概念

  1. 数据结构特性

    • 后进先出(LIFO):最后压入栈的元素最先弹出,与队列的 FIFO(先进先出)特性形成对比。
    • 核心操作:push(E)(压栈)、pop()(弹出栈顶元素)、peek()(获取栈顶元素不弹出)。
  2. 直观示例

    通过 ASCII 图示演示栈的压入和弹出过程,形象化 LIFO 机制。

二、Java 中 Stack 的实现

  1. 推荐使用 Deque 接口

    • Deque(双端队列)模拟 Stack 功能,常用方法:
      push(E)/addFirst(E)(压栈);pop()/removeFirst()(弹出);peek()/peekFirst()(查看栈顶)。
    • 最佳实践 :仅调用push()/pop()/peek(),避免使用 Deque 的其他方法,保持代码清晰。
  2. 避免使用遗留类Stack

    因 Java 早期设计遗留的Stack类兼容性问题,推荐使用 Deque 替代。

三、Stack 的典型应用场景

  1. JVM 方法调用栈

    • 维护方法调用层次:参数压栈、返回值压栈,嵌套调用过深会引发StackOverflowError(如无限递归)。
  2. 进制转换(以十进制转十六进制为例)

    • 步骤:循环取余压栈,直至商为 0,再依次弹出栈元素组成结果字符串。
    • 示例:12500转十六进制,通过反复除以 16 取余,最终弹出栈得到30D4
  3. 中缀表达式编译与计算

    • 中缀转后缀 :利用栈处理运算符优先级和括号,生成后缀表达式(如1 + 2*(9-5)1 2 9 5 - * +)。
    • 后缀表达式计算:从左到右扫描,遇数字压栈,遇运算符弹出操作数计算后压栈,最终栈顶为结果。

Java 迭代器(Iterator)使用详解

一、核心概念

  1. Iterator 本质

    • 是 Java 集合遍历的统一抽象模型,for each 循环本质上由编译器转换为 Iterator 遍历。

    • 示例:for (String s : list) 会被编译为:

      java 复制代码
      for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
          String s = it.next();
      }
  2. 核心接口

    • Iterable :集合类需实现此接口(定义 iterator() 方法),以支持 for each 循环。
    • Iterator :定义遍历逻辑,包含 hasNext()(判断是否有下一个元素)和 next()(获取下一个元素)方法。

二、Iterator 优势

  1. 统一访问方式

    调用方无需关心集合内部存储结构(如 ArrayList 的数组、LinkedList 的链表),以相同代码遍历 ListSetQueue 等。

  2. 解耦与通用性

    • 避免依赖集合特定接口(如 get(int)),例如 Set 无索引,无法用索引遍历,但可统一用 Iterator。
    • 提升代码复用性,更换集合类型时无需修改遍历逻辑。
  3. 高效性

    集合内部创建的 Iterator 可针对自身结构优化遍历(如 LinkedList 的 Iterator 避免低效的 get(int))。

三、自定义集合使用 Iterator

若需让自定义集合支持 for each 循环,需满足:

  1. 实现 Iterable 接口

    • 重写 iterator() 方法,返回自定义的 Iterator 实例。
  2. 实现 Iterator 接口

    • 在内部类中实现 hasNext()next(),定义具体遍历逻辑(如倒序遍历)。

示例代码

java 复制代码
class ReverseList<T> implements Iterable<T> {
    private List<T> list = new ArrayList<>();
    // 实现 Iterable 接口
    @Override
    public Iterator<T> iterator() {
        return new ReverseIterator(list.size()); // 返回自定义 Iterator
    }
    // 内部类实现 Iterator 接口(倒序遍历)
    class ReverseIterator implements Iterator<T> {
        int index;
        ReverseIterator(int index) { this.index = index; }
        @Override public boolean hasNext() { return index > 0; }
        @Override public T next() { return list.get(--index); }
    }
}

四、关键要点

  1. for each 的本质

    依赖 IterableIterator 接口,编译器自动生成遍历代码。

  2. 分离职责

    集合类负责存储数据,Iterator 负责遍历逻辑,调用方只需关注业务逻辑。

  3. 适用场景

    所有 Java 集合类(ListSetQueueMap 的键 / 值)均支持 Iterator,推荐优先使用以提升代码健壮性。

使用 Collections

一、Collections 工具类概述

  • 定位 :JDK 提供的工具类,位于java.util包,提供一系列操作集合的静态方法。
  • 注意 :名称为Collections(结尾有s),与Collection接口不同。

二、核心功能与方法

1. 创建特殊集合
  • 空集合(旧版 JDK)

    • emptyList():创建不可变空List
    • emptyMap():创建不可变空Map
    • emptySet():创建不可变空Set
    • 特点:返回的集合不可修改(添加 / 删除会抛出异常)。
  • 空集合(新版 JDK≥9)

    • 直接使用List.of()Map.of()Set.of()创建空集合。
  • 单元素集合(旧版 JDK)

    • singletonList(T o):创建含单个元素的不可变List
    • singletonMap(K key, V value):创建含单个键值对的不可变Map
    • singleton(T o):创建含单个元素的不可变Set
    • 新版替代List.of(T...)Map.of(T...)Set.of(T...)支持创建任意数量元素的不可变集合。
2. 集合操作
  • 排序

    • 方法:Collections.sort(List<T> list),直接修改传入的可变List,按自然顺序或自定义比较器排序。
  • 洗牌(随机打乱顺序)

    • 方法:Collections.shuffle(List<T> list),随机重排List元素顺序。
3. 不可变集合封装
  • 方法

    • unmodifiableList(List<? extends T> list):封装可变List为不可变List
    • unmodifiableSet(Set<? extends T> set):封装可变Set为不可变Set
    • unmodifiableMap(Map<? extends K, ? extends V> m):封装可变Map为不可变Map
  • 特性

    • 通过代理对象拦截修改操作,禁止对封装后的集合执行增删改操作(会抛出UnsupportedOperationException)。
    • 原始集合的修改会影响封装后的 "不可变" 集合,建议封装后丢弃原始集合引用。
4. 线程安全集合(过时方法)
  • 方法

    • synchronizedList(List<T> list):将List转为线程安全版本
    • synchronizedSet(Set<T> s):将Set转为线程安全版本
    • synchronizedMap(Map<K,V> m):将Map转为线程安全版本
  • 现状 :Java 5 后引入更高效的并发集合类(如ConcurrentHashMap),这些同步方法已较少使用。

相关推荐
paopaokaka_luck12 分钟前
智能推荐社交分享小程序(websocket即时通讯、协同过滤算法、时间衰减因子模型、热度得分算法)
数据库·vue.js·spring boot·后端·websocket·小程序
留不住丨晚霞20 分钟前
说说SpringBoot常用的注解?
java·开发语言
华科云商xiao徐26 分钟前
Java多线程爬虫动态线程管理实现
java·爬虫·数据挖掘
程序员NEO28 分钟前
Spring AI 对话记忆大揭秘:服务器重启,聊天记录不再丢失!
人工智能·后端
用户214118326360228 分钟前
惊爆!国内轻松白嫖 Claude Code,编程效率狂飙
后端
iccb101334 分钟前
我是如何实现在线客服系统的极致稳定性与安全性的
前端·javascript·后端
柒七爱吃麻辣烫35 分钟前
八股文系列-----SpringBoot自动配置的流程
java·spring boot·rpc
M1A140 分钟前
Java 面试系列第一弹:基础问题大盘点
java·后端·mysql
发仔12340 分钟前
Dubbo介绍及示例用法
java·dubbo
goxingman1 小时前
关于使用idea打包的时候报错,Maven提示乱码java: �Ҳ�������
java·maven·intellij-idea