当前位置:首页 > 文章列表 > 文章 > java教程 > Java 有序集合怎样统一访问首尾元素

Java 有序集合怎样统一访问首尾元素

来源:17golang原创 2026-10-18 00:50:33 0浏览 收藏

如果业务代码同时接收 List、Deque 和保持插入顺序的集合,过去常见的写法是分别调用 get(0)、get(size() - 1)、peekFirst() 或迭代器。Java 21 引入的 SequencedCollection 把“有明确遇到顺序、能访问两端、可以反向查看”统一成一个接口。

因此,统一访问首尾元素的做法是:把参数声明为 SequencedCollection,读取时使用 getFirst() 和 getLast(),需要改动时使用对应的 add 或 remove 方法,反向遍历时使用 reversed()。空集合和不可修改集合仍然有明确的异常边界,不能因为接口统一就省略处理。

官方参考地址:https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/SequencedCollection.html

先确认集合是否有明确遇到顺序

SequencedCollection 不是所有 Collection 的父接口。它要求集合拥有稳定的 encounter order,也就是可以解释哪个元素在前、哪个元素在后。官方 API 中,List、Deque、SortedSet 和 SequencedSet 都属于这条接口体系;常见的 ArrayList、LinkedList、LinkedHashSet 和 TreeSet 也在已知实现或继承关系中。

SequencedCollection 与 List、Deque、SortedSet、LinkedHashSet 以及首尾操作和反向视图的静态关系图
图1:Java 有序集合接口与首尾能力的静态关系图;这是说明图,不是运行截图。

接口参数可以这样收窄:

import java.util.SequencedCollection;
import java.util.List;

public final class BoundaryReader {
    public static  String describe(SequencedCollection values) {
        // 先把空集合当作调用方约束处理,避免 getFirst/getLast 抛出异常。
        if (values.isEmpty()) {
            return "empty";
        }

        // 这两个方法表达的是遇到顺序的两端,不依赖具体实现是数组还是链表。
        return "first=" + values.getFirst() + ", last=" + values.getLast();
    }

    public static void main(String[] args) {
        // List 在 Java 21+ 中实现了 SequencedCollection 的首尾契约。
        List queue = List.of("compile", "package", "publish");
        System.out.println(describe(queue));
    }
}

这里的重点不是把所有集合都改成同一个实现,而是让调用方依赖“有序集合”这项能力。若参数只需要判断包含关系,就继续使用更宽的 Collection;只有需要首尾或反向顺序时,才使用 SequencedCollection。

用 getFirst 和 getLast 读取两端

getFirst() 与 getLast() 的语义比手写索引更清楚。对于 List,官方实现分别对应第 0 个元素和 size() - 1 个元素;对于其他有序集合,调用方不需要知道其内部存储方式。两个方法在集合为空时都会抛出 NoSuchElementException,所以应该在边界处显式选择“提前返回”还是“让异常交给上层”。

import java.util.NoSuchElementException;
import java.util.SequencedCollection;

public final class EndpointReader {
    public static  E requireLast(SequencedCollection values) {
        // 这个方法的契约是“必须存在最后一个元素”,异常可以交给调用方记录。
        return values.getLast();
    }

    public static  E findFirstOrNull(SequencedCollection values) {
        // 查询型调用不把空集合当作系统故障,先返回 null 表示没有结果。
        if (values.isEmpty()) {
            return null;
        }
        return values.getFirst();
    }

    public static  E readWithBoundary(SequencedCollection values) {
        try {
            // 需要保留统一异常语义时,直接调用 getFirst。
            return values.getFirst();
        } catch (NoSuchElementException ex) {
            // 这里补充业务上下文,避免上层只看到一个无描述的异常。
            throw new IllegalStateException("集合不能为空", ex);
        }
    }
}

生产代码里不建议为了“保险”同时使用索引和首尾方法。列表已经是 SequencedCollection 时,统一使用首尾方法能让代码在切换到 Deque 或有序集合实现后保持同样的调用面。

用四个两端方法表达修改意图

除了读取,接口还提供 addFirst、addLast、removeFirst 和 removeLast。它们都是可选操作:集合实现可以不支持修改,或者只支持其中一部分。因此声明为 SequencedCollection 后,仍然不能假定任何集合都可写。

import java.util.ArrayList;
import java.util.SequencedCollection;

public final class EndpointMutation {
    public static  void addAuditMarkers(SequencedCollection values,
                                             E firstMarker, E lastMarker) {
        // 只有调用方确认集合可修改时,才使用两端插入操作。
        values.addFirst(firstMarker);
        values.addLast(lastMarker);
    }

    public static  E takeFirst(SequencedCollection values) {
        // removeFirst 同时完成读取和删除,空集合会抛出 NoSuchElementException。
        return values.removeFirst();
    }

    public static void main(String[] args) {
        // ArrayList 支持这些操作,但头部插入或删除可能移动后续元素。
        ArrayList values = new ArrayList<>();
        values.add("middle");
        addAuditMarkers(values, "first", "last");
        System.out.println(values);
    }
}

实现选择也要看访问模式:频繁操作两端时,Deque 通常更贴近意图;需要索引访问时,List 更直观。接口统一解决的是调用语义,不会把不同实现的时间复杂度自动变成相同。

用 reversed 获取反向视图

reversed() 返回按相反遇到顺序观察原集合的视图。它不是要求调用方复制一份新集合,因此适合只需要反向遍历、从末端读取或把现有逻辑转成反向方向的场景。

import java.util.ArrayList;
import java.util.List;

public final class ReverseViewExample {
    public static void main(String[] args) {
        List stages = new ArrayList<>(List.of("compile", "test", "publish"));

        // reversed 返回反向顺序的视图,原列表仍然是同一个数据来源。
        List reverse = stages.reversed();
        System.out.println(reverse); // [publish, test, compile]
        System.out.println(reverse.getFirst()); // publish
        System.out.println(reverse.getLast());  // compile

        // 视图是否允许修改取决于底层列表;不要把 reversed 当成不可变副本。
        reverse.set(0, "rollback");
        System.out.println(stages); // [compile, test, rollback]
    }
}

反向视图的首尾也会反转:原集合的最后一个元素,在视图里就是第一个元素。官方文档还说明,若实现允许视图修改,修改会写回底层集合;底层集合的变更是否立即对视图可见,则取决于具体实现。需要快照时,应显式复制,例如 new ArrayList<>(values.reversed()),而不是误以为 reversed() 已经完成复制。

把生产环境边界写进检查清单

SequencedCollection 的首尾读取、可选修改、空集合异常、不可修改集合和反向视图边界图
图2:首尾操作与异常边界的静态关系图;这是说明图,不是运行结果。

引入统一接口后,发布前最值得检查的不是方法名是否记住,而是调用对象是否满足边界条件:

检查项需要确认的事实处理建议
JDK 版本这些方法自 Java 21 起可用编译工具链和运行时都升级到支持的版本,或保留旧版本适配层
空集合读取和删除首尾会抛出 NoSuchElementException查询场景先判断 isEmpty,必需数据场景保留带上下文的异常
修改权限两端修改是可选操作对 List.of 等不可修改集合不要调用 add/remove;必要时先复制
反向视图reversed 返回视图,不等于深拷贝需要独立快照时显式创建新集合
性能预期统一接口不改变底层实现的复杂度根据两端操作、索引访问和迭代模式选择 List 或 Deque

如果项目仍要兼容 Java 17,不能直接把 getFirst() 或 reversed() 写进目标字节码;可以在边界层继续使用旧接口,待运行时与编译器升级后再切换。这个兼容判断应在构建配置和发布清单里固定下来,避免开发机能编译、生产环境却无法启动。

常见问题

SequencedCollection 和 Collection 有什么区别?

Collection 只表达元素集合;SequencedCollection 还表达明确的遇到顺序、两端操作和反向视图。没有顺序要求的参数不要强行升级为后者。

getFirst 能替代所有的 get(0) 吗?

只有当调用者关心“第一个元素”而不是固定索引时才适合替代。若业务确实需要第 3 个或第 N 个元素,仍然使用列表的索引 API。

reversed 会创建新集合吗?

不会把它当作新集合。它返回反向顺序的视图,读写行为和底层变更可见性由具体实现决定;需要隔离数据时请显式复制。

为什么 LinkedHashSet 也能统一访问首尾?

它维护明确的遇到顺序,并属于有序集合接口体系。统一接口让调用方不必依赖“链表”或“数组”这样的内部实现名称。

总结一下:需要按顺序访问两端时,把依赖声明为 SequencedCollection;读取用 getFirst/getLast,修改用四个两端方法,反向查看用 reversed。最后把 JDK 版本、空集合、可修改性、视图语义和底层复杂度写入发布检查清单,这样接口升级才不会停留在方法名替换。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
PHP PDO 怎样避免 bindValue 类型导致查询异常PHP PDO 怎样避免 bindValue 类型导致查询异常
上一篇
PHP PDO 怎样避免 bindValue 类型导致查询异常
Java computeIfAbsent 递归更新时怎样避免死循环
下一篇
Java computeIfAbsent 递归更新时怎样避免死循环
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    510次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    570次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    595次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    547次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    364次使用