当前位置:首页 > 技术 > 正文内容

Java 集合体系详解:List 接口、遍历方式及底层原理

访客 技术 2026年6月21日 1

一、Collection 接口核心概览

在 Java 集合框架中,Collection 是单列集合的顶层接口。根据数据特性不同,主要分为两大体系:List(允许重复、有序、带索引)和 Set(禁止重复、无序、无索引)。

Collection体系结构图

以下是 Collection 接口中最常用的功能方法:

API 签名 功能说明
boolean add(E e) 向容器内追加新元素
boolean remove(Object o) 移除指定的对象实例
void clear() 清空容器内的所有数据
boolean contains(Object o) 检索特定元素是否存在
int size() 获取当前存储元素的总数

注意:在使用 containsremove 比较自定义对象时,建议重写类的 equals 方法,否则默认会比较内存地址。

二、集合遍历的多种策略

对集合数据的访问有多种模式,选择哪种取决于具体场景。

2.1 使用迭代器 (Iterator)

这是最基础的遍历方式,适用于所有实现了 Collection 接口的类。其核心流程包含两个步骤:检查是否有下一项 (hasNext) 以及获取该项 (next)。

package com.example.traversal;

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

public class BasicIteration {
    public static void main(String[] args) {
        // 初始化容器
        Collection<String> repository = new ArrayList<>();
        repository.addAll(java.util.Arrays.asList("ProjectA", "ProjectB", "ProjectC"));

        // 获取游标对象
        Iterator<String> cursor = repository.iterator();
        
        // 循环读取
        while (cursor.hasNext()) {
            String currentTask = cursor.next();
            System.out.println("处理任务:" + currentTask);
        }
        
        // 安全性提示:在遍历过程中直接调用集合的 remove 会抛出异常
        // 必须通过迭代器自带的 remove() 方法来删除当前节点
    }
}

如果在遍历过程中需要动态删除元素,必须调用 iterator.remove(),而不是集合对象的 remove()

public class SafeRemoval {
    public static void main(String[] args) {
        ArrayList<Integer> nums = new ArrayList<>();
        for(int i=0; i<5; i++) nums.add(i);

        Iterator<Integer> walker = nums.iterator();
        while(walker.hasNext()){
            int val = walker.next();
            if(val % 2 == 0) { // 移除偶数
                walker.remove(); 
            }
        }
        System.out.println(nums);
    }
}

2.2 增强型 for 循环

JDK 5 引入的语法糖,底层依然依赖迭代器实现,但代码更简洁,适合只读遍历。

public class ForEachSyntax {
    public static void main(String[] args) {
        ArrayList<String> products = new ArrayList<>();
        products.add("Laptop");
        products.add("Mouse");
        
        for (String item : products) {
            System.out.println("库存物品:" + item);
        }
    }
}

2.3 Lambda 表达式与函数式遍历

利用 forEach 方法配合 Consumer 接口,可以以声明式风格执行逻辑。

public class FunctionalTraversal {
    public static void main(String[] args) {
        Collection<String> users = new ArrayList<>();
        users.add("Admin");
        users.add("User1");
        
        // 传统匿名内部类写法
        /*users.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });*/
        
        // Lambda 简化写法
        users.forEach(name -> System.out.println("Hello, " + name));
    }
}

三、List 接口的高级操作

List 继承自 Collection,增加了基于索引的控制能力。主要特征包括:有序性、元素可重复、支持随机访问。

方法 行为描述
void add(int index, E element) 在指定下标位置插入新元素
E get(int index) 获取指定下标的元素
E set(int index, E element) 替换指定下标的元素,返回旧值
E remove(int index) 删除指定下标的元素,返回被删值
public class ListIndexOps {
    public static void main(String[] args) {
        List<String> tasks = new ArrayList<>();
        tasks.add("Task 1");
        tasks.add("Task 2");
        
        // 插入到索引 0 处,原有元素后移
        tasks.add(0, "Priority Task");
        
        // 修改索引 1 处的内容
        String oldVal = tasks.set(1, "Modified Task");
        
        // 删除索引 2 处的内容
        tasks.remove(2);
        
        System.out.println(tasks.get(0));
    }
}

重载陷阱说明: remove 方法存在重载版本,一个是接收 Object(删除指定值的第一个匹配项),另一个接收 int(删除指定位置的元素)。若传入的是 Integer 对象,会被当做 Object 处理;若传入字面量数字,可能被当做索引处理,需谨慎区分。

四、数据结构视角的实现差异

4.1 数组链表对比理论

  • 栈 (Stack):后进先出 (LIFO)。
  • 队列 (Queue):先进先出 (FIFO)。
  • 数组 (Array):内存连续,查询效率高 (O(1)),但在中间插入/删除性能差,需要移动大量数据。
  • 链表 (Linked List):内存不连续,查询慢 (O(n)),但增删操作只需修改指针指向,效率较高。

4.2 ArrayList 实现机制

ArrayList 基于动态数组实现。初始创建时,内部维护一个长度为 10 的数组(elementData)和一个计数器(size)。

  • 扩容策略:当空间不足时,会自动分配新数组。新容量通常是旧容量的 1.5 倍(oldCapacity + (oldCapacity >> 1))。
  • 极端情况:如果需要一次性添加大量元素导致 1.5 倍仍不足,则直接按需求容量分配。
  • 代价:每次扩容都涉及 System.arraycopy 拷贝,频繁扩容会影响性能。

4.3 LinkedList 实现机制

LinkedList 基于双向链表实现。每个节点包含三个部分:前驱节点引用、数据域、后继节点引用。

  • 头尾指针:内部维护 firstlast 指针指向首尾节点。
  • 插入过程:新增节点只需调整相邻节点的指针引用,无需移动数据。
  • 特有方法:由于链表结构,它提供了如 addFirst, addLast, removeFirst 等便捷方法,使其也能作为队列或双端队列使用。
// LinkedList 独有 API 示例
public class DqueuOperations {
    public static void main(String[] args) {
        LinkedList<String> deque = new LinkedList<>();
        deque.addFirst("Header");
        deque.addLast("Footer");
        
        System.out.println(deque.getFirst());
        deque.removeLast();
    }
}

4.4 迭代器源码简析

无论是 ArrayList 还是 LinkedList,其迭代器均遵循 Iterator 接口规范。源码层面通常维护了一个期望修改计数(expectedModCount)和当前游标位置(cursor)。若在遍历时集合发生结构性变化且非由迭代器自身引起,下次调用时便会抛出 ConcurrentModificationException,以保障一致性。

标签: Java

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。