Day22 - 集合 - 2.List集合

2.List集合

2.1List集合的概述和特点【记忆】

  • List集合的概述

    • 有序集合,这里的有序指的是存取顺序

    • 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素

    • 与Set集合不同,列表通常允许重复的元素

  • List集合的特点

    • 存取有序

    • 可以重复

    • 有索引

🧠 理论理解
List 集合是 Collection 接口的重要子接口,它的核心特征是“有序、可重复”。所谓有序,是指元素的存储顺序与取出顺序保持一致,集合内部使用索引(从 0 开始)定位每个元素。与 Set 不同,List 允许添加重复元素,是典型的“顺序容器”。此外,List 是实现随机访问的基础数据结构,支持按索引增删改查,是日常开发中最常用的集合类型之一。

🏢 企业实战理解

  • 字节跳动:在抖音推荐系统中,List 被用来保存用户历史点击序列,确保推荐逻辑按时间顺序回溯。

  • 阿里巴巴:天猫购物车系统使用 List 保存当前用户已添加的商品项,确保显示顺序与添加顺序一致。

  • Google:Gmail 邮件列表分页缓存中利用 List 存储预加载的邮件对象,提高 UI 响应速度。

  • OpenAI:在 ChatGPT 的上下文窗口内,用户输入历史和 AI 回复历史按 List 顺序组合,作为大模型输入。

  • 美团:美团骑手接单的队列是通过 List 实现的,以便骑手可以看到顺序任务列表。

 

面试题 1️⃣:请详细说一下 List 集合的特点,并和 Set 做对比。

参考答案
List 是 Collection 接口的重要分支,具有以下几个特点:

  • 有序性:List 存储元素的顺序和取出顺序一致,基于索引定位数据。

  • 可重复性:允许存储重复元素。

  • 支持索引访问:提供 get、set、add(index, element)、remove(index) 等基于位置的操作。

Set 不允许元素重复,内部实现常见如 HashSet 是无序的,LinkedHashSet 保持插入顺序,TreeSet 有序但基于自然顺序或 Comparator 排序。

大厂扩展
字节跳动的抖音评论功能,采用 List 存储展示顺序,而“黑名单”去重列表则用 Set。实际开发中两者往往组合使用,保证既有顺序又有去重机制。

场景题 1️⃣:你在字节跳动做抖音短视频评论系统,产品要求评论展示要保持用户输入的顺序,同时允许重复评论内容。此时该用什么集合?为什么?

场景答案
这种场景需要满足两个要求:
1️⃣ 顺序性 → 显示顺序必须和存储顺序一致;
2️⃣ 可重复性 → 不限制相同评论(比如用户连续打“666”)。

因此应选择 List 集合。相比 Set,List 允许重复元素,且保证插入顺序不变,完全符合评论列表的业务特性。字节跳动内部也采用了 ArrayList 封装数据展示层,并在服务端通过分页控制列表大小,提升加载速度。

 

2.2List集合的特有方法【应用】

  • 方法介绍

    方法名描述
    void add(int index,E element)在此集合中的指定位置插入指定的元素
    E remove(int index)删除指定索引处的元素,返回被删除的元素
    E set(int index,E element)修改指定索引处的元素,返回被修改的元素
    E get(int index)返回指定索引处的元素
  • 示例代码

    public class MyListDemo {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("aaa");
            list.add("bbb");
            list.add("ccc");
            //method1(list);
            //method2(list);
            //method3(list);
            //method4(list);
        }
    ​
        private static void method4(List<String> list) {
            //        E get(int index)      返回指定索引处的元素
            String s = list.get(0);
            System.out.println(s);
        }
    ​
        private static void method3(List<String> list) {
            //        E set(int index,E element)    修改指定索引处的元素,返回被修改的元素
            //被替换的那个元素,在集合中就不存在了.
            String result = list.set(0, "qqq");
            System.out.println(result);
            System.out.println(list);
        }
    ​
        private static void method2(List<String> list) {
            //        E remove(int index)       删除指定索引处的元素,返回被删除的元素
            //在List集合中有两个删除的方法
            //第一个 删除指定的元素,返回值表示当前元素是否删除成功
            //第二个 删除指定索引的元素,返回值表示实际删除的元素
            String s = list.remove(0);
            System.out.println(s);
            System.out.println(list);
        }
    ​
        private static void method1(List<String> list) {
            //        void add(int index,E element) 在此集合中的指定位置插入指定的元素
            //原来位置上的元素往后挪一个索引.
            list.add(0,"qqq");
            System.out.println(list);
        }
    }

🧠 理论理解
List 接口在 Collection 的基础上扩展了四个关键方法,允许基于索引直接操作元素:

  • add(index, element):插入元素到指定位置,后续元素右移。

  • remove(index):删除指定索引处的元素,并返回被删除的对象。

  • set(index, element):替换指定索引的元素,返回原来的元素。

  • get(index):按索引获取元素。

这些方法提供了 List 的随机访问能力,使其比 Set 更适合有序数据场景。

🏢 企业实战理解

  • 字节跳动:今日头条 APP 内部评论列表在前端缓存时,通过 set() 方法局部刷新点赞状态,避免全量更新。

  • 阿里巴巴:支付宝账单列表在查询后,通过 add() 方法插入“广告位”或“提示卡片”,位置是动态可控的。

  • 英伟达:GPU 分布式任务队列用 remove() 方法动态摘除已完成的任务,保证高并发下任务分发的有序性。

  • Google Drive:文件夹内的文件顺序重排功能用 set() 实现,支持拖拽式的 UI 排序。

  • 滴滴出行:司机端的待处理乘客订单列表用 List 存储,当用户取消订单时用 remove() 精确删除。

 

面试题 2️⃣add(index, element) 在 ArrayList 中底层是怎么实现的?

参考答案
在 ArrayList 中,add(index, element) 需要先确保容量足够(如果容量不足会触发扩容机制),然后调用 System.arraycopy() 将 index 及其后面的元素整体向右平移一位,空出位置插入新元素。时间复杂度为 O(n)。

大厂思路
阿里巴巴在购物车功能中,通过提前预估列表容量(如常买清单默认长度),减少 arraycopy 触发的次数,提高性能。

场景题 2️⃣:在美团开发“我的收藏”功能时,用户可以手动调整收藏列表的顺序。如何用 List 实现“在第 2 个位置插入新收藏”功能?涉及哪个方法?

场景答案
这个功能对应的核心就是指定位置插入元素add(int index, E element) 方法可以做到。例如:

favorites.add(1, "新收藏");

在美团中,这类“自定义顺序”的收藏数据会存储用户自定义顺序值,插入时不仅在内存 List 操作,还会同步更新数据库里的排序字段,确保前后端一致性。

 

2.3List集合的五种遍历方式【应用】

  1. 迭代器

  2. 列表迭代器

  3. 增强for

  4. Lambda表达式

  5. 普通for循环

代码示例:

//创建集合并添加元素
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
​
//1.迭代器
/*Iterator<String> it = list.iterator();
     while(it.hasNext()){
        String str = it.next();
        System.out.println(str);
}*/
​
​
//2.增强for
//下面的变量s,其实就是一个第三方的变量而已。
//在循环的过程中,依次表示集合中的每一个元素
/* for (String s : list) {
       System.out.println(s);
   }*/
​
//3.Lambda表达式
//forEach方法的底层其实就是一个循环遍历,依次得到集合中的每一个元素
//并把每一个元素传递给下面的accept方法
//accept方法的形参s,依次表示集合中的每一个元素
//list.forEach(s->System.out.println(s) );
​
​
//4.普通for循环
//size方法跟get方法还有循环结合的方式,利用索引获取到集合中的每一个元素
/*for (int i = 0; i < list.size(); i++) {
            //i:依次表示集合中的每一个索引
            String s = list.get(i);
            System.out.println(s);
        }*/
​
// 5.列表迭代器
//获取一个列表迭代器的对象,里面的指针默认也是指向0索引的
​
//额外添加了一个方法:在遍历的过程中,可以添加元素
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
    String str = it.next();
    if("bbb".equals(str)){
        //qqq
        it.add("qqq");
    }
}
System.out.println(list);

🧠 理论理解
List 支持 5 种遍历方式:
1️⃣ Iterator
2️⃣ 增强 for
3️⃣ Lambda 表达式(forEach)
4️⃣ 普通 for 循环(通过索引)
5️⃣ ListIterator(支持遍历时添加元素)

不同遍历方式适用于不同场景,例如:

  • 普通 for 提供高效的索引访问。

  • Iterator 支持安全删除元素。

  • ListIterator 提供双向遍历和遍历中插入的功能。

🏢 企业实战理解

  • 京东:商品列表页用普通 for 循环遍历商品数据,按索引映射到 UI 元素,提升性能。

  • 字节跳动:西瓜视频播放器缓存历史记录时,使用 Lambda + forEach 快速遍历播放列表进行统计。

  • 腾讯:QQ 聊天消息回放时用 ListIterator 实现“向上翻页”功能,支持向前检索消息记录。

  • 美团外卖:骑手任务队列的遍历通过 Iterator 实现,支持任务超时后自动移除。

  • OpenAI:在生成多轮对话时,用增强 for 快速遍历对话历史,实现上下文整合。

 

面试题 3️⃣:ListIterator 和 Iterator 有什么区别?使用场景是?

参考答案

  • Iterator:只能单向遍历,只支持删除元素。

  • ListIterator:双向遍历(有 hasPrevious()previous() 方法),支持在遍历中动态添加、修改元素,还能获取当前索引。

使用场景

  • 如果只是简单遍历+删除,用 Iterator;

  • 如果有需求在遍历中插入/修改,或需要反向遍历,就要用 ListIterator。

大厂补充
腾讯视频评论翻页功能利用 ListIterator 的反向遍历高效加载历史数据,避免重新构造列表。

场景题 3️⃣:你在 Google Photos 团队开发“相册时间线”功能,展示用户照片时需要反向遍历最近 1 年的照片列表。哪种遍历方式最合适?为什么?

场景答案
这里推荐使用 ListIterator,它支持双向遍历,通过 hasPrevious()previous() 方法可以反向读取数据,不需要再手动倒序或重组数据,非常高效。

代码示例:

ListIterator<Photo> iterator = photoList.listIterator(photoList.size());
while(iterator.hasPrevious()) {
    Photo p = iterator.previous();
    // 处理照片
}

在 Google Photos,时间线按时间倒序显示,ListIterator 非常适合这种“最新数据优先展示”的场景。

 

2.4 细节点注意:

List系列集合中的两个删除的方法

1.直接删除元素
2.通过索引进行删除

代码示例:

//List系列集合中的两个删除的方法
//1.直接删除元素
//2.通过索引进行删除
​
//1.创建集合并添加元素
List<Integer> list = new ArrayList<>();
​
list.add(1);
list.add(2);
list.add(3);
​
​
//2.删除元素
//请问:此时删除的是1这个元素,还是1索引上的元素?
//为什么?
//因为在调用方法的时候,如果方法出现了重载现象
//优先调用,实参跟形参类型一致的那个方法。
​
//list.remove(1);
​
​
//手动装箱,手动把基本数据类型的1,变成Integer类型
Integer i = Integer.valueOf(1);
​
list.remove(i);
​
System.out.println(list);
​

🧠 理论理解
List 集合中 remove() 方法重载了两种形式:
1️⃣ remove(Object o) 删除指定对象(按值删除)。
2️⃣ remove(int index) 删除指定索引(按位置删除)。

⚠️ 注意:当传入的是数字时,Java 会优先匹配 remove(int index),这在删除 Integer 类型时容易出错,常见的做法是用 Integer.valueOf() 强制将数字装箱为对象类型。

🏢 企业实战理解

  • 支付宝:删除收藏夹的数字 ID 时,开发团队在早期曾踩坑误用 remove(1) 删除索引,后用 Integer.valueOf() 修复。

  • 滴滴:司机端订单删除时,为区分订单序号和订单对象,严格封装了 remove 方法防止误删索引。

  • Google:安卓通讯录应用中,用 remove(Object o) 精确删除联系人对象,避免索引错误导致的数据错位问题。

 

面试题 4️⃣:如何在 List<Integer> 中删除指定值 1,而不是索引 1 位置的元素?解释原因。

参考答案
因为 List 的 remove() 方法存在重载:

  • remove(int index):按索引删除

  • remove(Object o):按对象删除

如果写 list.remove(1),会被解析为删除索引 1 处的元素。为了删除值为 1 的元素,应该强制装箱:

list.remove(Integer.valueOf(1));

这样会匹配 remove(Object o)

大厂思路
字节跳动在后台大数据清洗时,团队踩过这个坑,在删除 Integer 列表中指定值时出错,后来封装工具类明确类型,防止误删索引。

场景题 4️⃣:你在腾讯视频开发收藏夹功能,遇到一个 Bug:删除 List<Integer> 中的 1 时,删除的是索引 1 处的元素,而不是值为 1 的元素。请问你如何修改代码?

场景答案
问题出现的原因是 remove(int index)remove(Object o) 方法重载了。如果你直接写:

list.remove(1);

它会认为是删除索引 1的元素。

解决方案:要删除值为 1 的元素,必须手动装箱,写成:

list.remove(Integer.valueOf(1));

在腾讯内部,这种问题常出现在整型列表处理上,团队通过工具方法封装 removeByValue() 来隐藏细节,防止这种 Bug 重复发生。

 

3.数据结构

3.1数据结构之栈和队列【记忆】

  • 栈结构

    先进后出

  • 队列结构

    先进先出

🧠 理论理解

  • 栈(Stack):后进先出(LIFO),常见于撤销/回退功能。

  • 队列(Queue):先进先出(FIFO),适用于排队处理任务,如消息队列。

🏢 企业实战理解

  • 字节跳动:抖音短视频撤销功能用栈实现回退,任务分发队列用队列结构高效处理视频上传。

  • 阿里巴巴:蚂蚁金服风控队列使用高并发的阻塞队列保障实时性。

  • OpenAI:API 请求接入层用任务队列排队执行,缓冲高峰请求。

 

面试题 5️⃣:什么场景下优先使用栈,什么场景下优先使用队列?请举实际项目案例。

参考答案

  • 栈(LIFO):撤销、递归、回溯问题。

  • 队列(FIFO):消息排队、限流、任务调度。

实际案例

  • 栈:美团点评 App 实现“返回上一步”功能时,使用栈记录操作路径。

  • 队列:字节跳动内容审核系统中,用队列管理待审核视频任务,保证顺序执行。

场景题 5️⃣:你在 OpenAI 的 GPT-4 编辑器中实现“撤销/重做”功能,打算用 List 实现栈结构,具体怎么操作?

场景答案
撤销/重做是栈结构典型场景,先进后出(LIFO)。可以用 LinkedList 来模拟栈:

  • 撤销操作:用 addFirst() 存储每次编辑;

  • 重做操作:用 removeFirst() 弹出上一步。

示例代码:

LinkedList<EditAction> undoStack = new LinkedList<>();
undoStack.addFirst(newAction); // 记录操作
EditAction last = undoStack.removeFirst(); // 撤销

OpenAI 的实际产品中,这种“多步回退”功能都是基于栈实现,并且在多线程编辑场景中配合锁机制保证并发安全。

 

3.2数据结构之数组和链表【记忆】

  • 数组结构

    查询快、增删慢

  • 队列结构

    查询慢、增删快

 

🧠 理论理解

  • 数组:连续内存,支持随机访问,增删慢。

  • 链表:节点指针连接,增删快,随机访问慢。

🏢 企业实战理解

  • 美团外卖:用户界面展示订单列表用数组存储,方便随机访问。

  • 腾讯会议:实时消息队列链表实现,插入删除效率高。

  • 英伟达:GPU 作业链表存储任务依赖,支持动态插入任务。

 

面试题 6️⃣:什么时候用数组?什么时候用链表?请结合性能分析。

参考答案

  • 数组:支持 O(1) 随机访问,适合读多写少、数据固定长度场景。

  • 链表:支持 O(1) 插入/删除,适合频繁增删的场景,但访问性能差。

性能分析

  • 数组:增删需要整体移动,O(n)。

  • 链表:随机访问 O(n)。

实际案例
Google Docs 实现多人协作时,用链表维护操作日志,方便实时追加和回滚;而展示数据则用数组缓存提高访问速度。

场景题 6️⃣:你在英伟达实现 GPU 任务调度列表,高并发场景中任务频繁插入/删除,如何选择数据结构,为什么?

场景答案
此时应选择 链表(LinkedList),因为链表的插入/删除是 O(1) 的,不需要像数组那样移动大量数据。虽然查找是 O(n),但 GPU 任务调度往往只需头尾插入和删除,不需要随机访问,非常适合链表。

英伟达的 GPU 任务池内部会将任务挂在一个链表中,用锁和条件变量确保线程安全。

 

4.List集合的实现类

4.1List集合子类的特点【记忆】

  • ArrayList集合

    底层是数组结构实现,查询快、增删慢

  • LinkedList集合

    底层是链表结构实现,查询慢、增删快

🧠 理论理解

  • ArrayList:基于数组,查询快,增删慢。

  • LinkedList:基于链表,增删快,查询慢。

  • Vector:线程安全版 ArrayList(已较少使用)。

🏢 企业实战理解

  • 字节跳动:短视频缓存列表使用 ArrayList 提高读取速度。

  • 京东:活动排期任务用 LinkedList 快速动态插入/删除。

  • 腾讯:早期微信群聊列表用 Vector 线程安全实现。

 

面试题 7️⃣:简述 ArrayList 和 LinkedList 的主要区别?什么时候选用 LinkedList?

参考答案

  • ArrayList:基于数组实现,查询快(O(1)),插入/删除慢(O(n))。

  • LinkedList:基于双向链表,查询慢(O(n)),插入/删除快(O(1))。

使用时机

  • 如果场景主要是随机访问/查找 → ArrayList

  • 如果场景频繁插入/删除 → LinkedList

大厂补充
阿里巴巴秒杀系统中,库存展示页用 ArrayList;后台限流队列,用 LinkedList 提高实时性。

场景题 7️⃣:你在阿里巴巴开发“双 11 秒杀系统”时,数据存储层该选用 ArrayList 还是 LinkedList?为什么?

场景答案
选择 ArrayList,因为秒杀系统的页面展示需求是高频读取,几乎不涉及增删操作。ArrayList 基于数组结构,支持 O(1) 读取,性能更优。

阿里巴巴也在 Double 11 期间优化了秒杀系统,把订单队列用 LinkedList 实现(插入/删除多),展示列表则用 ArrayList。

 

4.2LinkedList集合的特有功能【应用】

  • 特有方法

    方法名说明
    public void addFirst(E e)在该列表开头插入指定的元素
    public void addLast(E e)将指定的元素追加到此列表的末尾
    public E getFirst()返回此列表中的第一个元素
    public E getLast()返回此列表中的最后一个元素
    public E removeFirst()从此列表中删除并返回第一个元素
    public E removeLast()从此列表中删除并返回最后一个元素
  • 示例代码

    public class MyLinkedListDemo4 {
        public static void main(String[] args) {
            LinkedList<String> list = new LinkedList<>();
            list.add("aaa");
            list.add("bbb");
            list.add("ccc");
    //        public void addFirst(E e) 在该列表开头插入指定的元素
            //method1(list);
    ​
    //        public void addLast(E e)  将指定的元素追加到此列表的末尾
            //method2(list);
    ​
    //        public E getFirst()       返回此列表中的第一个元素
    //        public E getLast()        返回此列表中的最后一个元素
            //method3(list);
    ​
    //        public E removeFirst()        从此列表中删除并返回第一个元素
    //        public E removeLast()     从此列表中删除并返回最后一个元素
            //method4(list);
          
        }
    ​
        private static void method4(LinkedList<String> list) {
            String first = list.removeFirst();
            System.out.println(first);
    ​
            String last = list.removeLast();
            System.out.println(last);
    ​
            System.out.println(list);
        }
    ​
        private static void method3(LinkedList<String> list) {
            String first = list.getFirst();
            String last = list.getLast();
            System.out.println(first);
            System.out.println(last);
        }
    ​
        private static void method2(LinkedList<String> list) {
            list.addLast("www");
            System.out.println(list);
        }
    ​
        private static void method1(LinkedList<String> list) {
            list.addFirst("qqq");
            System.out.println(list);
        }
    }

🧠 理论理解
LinkedList 独有的双端操作:

  • addFirst()/addLast()

  • getFirst()/getLast()

  • removeFirst()/removeLast()

适合实现队列、栈结构等场景。

🏢 企业实战理解

  • 美团:骑手抢单列表用 addLast() 插入订单,队首订单最先处理。

  • 字节跳动:直播弹幕队列通过 removeFirst() 实现实时展示。

  • Google Docs:协作编辑历史版本栈用 addFirst() 实现撤销功能。

 

面试题 8️⃣:LinkedList 如何实现队列和栈?请简述底层逻辑。

参考答案

  • 队列:用 addLast() 入队,removeFirst() 出队。

  • 栈:用 addFirst() 入栈,removeFirst() 出栈。

底层逻辑
LinkedList 通过双向链表实现,first/last 节点快速操作头尾,无需移动其他节点,时间复杂度 O(1)。

企业案例
美团骑手抢单列表实现时,用 LinkedList 的 addLast() 高效添加新任务,并用 removeFirst() 提供订单给骑手。

场景题 8️⃣:字节跳动飞书的“最近联系人”功能希望在最前面快速插入联系人,并能从末尾删除超时联系人,LinkedList 如何实现?

场景答案
利用 addFirst()removeLast()

  • 新联系人:list.addFirst(contact);

  • 删除超时联系人:list.removeLast();

双向链表的特性可以保证头尾插入删除都是 O(1) 操作,性能极佳。飞书类似的 IM 逻辑采用 LinkedList 实现这种滑动窗口式数据管理。

 

5. 源码分析

5.1 ArrayList源码分析:

核心步骤:

  1. 创建ArrayList对象的时候,他在底层先创建了一个长度为0的数组。

    数组名字:elementDate,定义变量size。

    size这个变量有两层含义: ①:元素的个数,也就是集合的长度 ②:下一个元素的存入位置

  2. 添加元素,添加完毕后,size++

扩容时机一:

  1. 当存满时候,会创建一个新的数组,新数组的长度,是原来的1.5倍,也就是长度为15.再把所有的元素,全拷贝到新数组中。如果继续添加数据,这个长度为15的数组也满了,那么下次还会继续扩容,还是1.5倍。

扩容时机二:

  1. 一次性添加多个数据,扩容1.5倍不够,怎么办呀?

    如果一次添加多个元素,1.5倍放不下,那么新创建数组的长度以实际为准。

举个例子: 在一开始,如果默认的长度为10的数组已经装满了,在装满的情况下,我一次性要添加100个数据很显然,10扩容1.5倍,变成15,还是不够,

怎么办?

此时新数组的长度,就以实际情况为准,就是110

具体分析过程可以参见视频讲解。

添加一个元素时的扩容:

 

添加多个元素时的扩容:

 

🧠 理论理解
ArrayList 内部是数组结构,默认容量为 10,扩容机制:

  • 单元素添加:容量不足时扩容为原容量 1.5 倍。

  • 批量添加:一次性扩容满足新数据所需容量。

🏢 企业实战理解

  • 字节跳动:列表预加载场景需关注扩容性能,防止频繁 resize 影响响应速度。

  • 阿里巴巴:购物车系统优化时,曾将 ArrayList 改为初始化指定容量,避免多次扩容。

  • OpenAI:大模型 token 窗口数据管理,初始化 ArrayList 时直接分配大容量,减少内存重分配开销。

 

面试题 9️⃣:ArrayList 的扩容机制是怎样的?为什么 1.5 倍扩容?

参考答案

  • 默认容量:10

  • 单个元素添加时,容量不足会扩容为 原容量 * 1.5

  • 大批量添加时,直接扩到“所需容量”。

扩容原因
1.5 倍扩容是权衡空间与时间复杂度的结果,防止频繁扩容的性能损耗,同时也不浪费太多内存。

大厂补充
字节跳动的推荐服务在缓存列表初始化时,直接初始化容量(如 new ArrayList<>(1000)),避免系统高峰期频繁扩容。

场景题 9️⃣:美团外卖首页推荐用了 ArrayList,发现高峰期频繁触发扩容导致卡顿。请问如何优化?

场景答案
优化建议:提前设置合理容量。通过 new ArrayList<>(预计容量) 直接初始化,避免反复扩容带来的性能问题。

List<Goods> list = new ArrayList<>(5000);

美团在外卖首页中通过预估数据规模,初始化容量后大大减少了卡顿现象。

 

5.2 LinkedList源码分析:

底层是双向链表结构

核心步骤如下:

  1. 刚开始创建的时候,底层创建了两个变量:一个记录头结点first,一个记录尾结点last,默认为null

  2. 添加第一个元素时,底层创建一个结点对象,first和last都记录这个结点的地址值

  3. 添加第二个元素时,底层创建一个结点对象,第一个结点会记录第二个结点的地址值,last会记录新结点的地址值

具体分析过程可以参见视频讲解。

 

🧠 理论理解
底层是双向链表,节点包含:

  • 上一个节点(prev)

  • 元素值(item)

  • 下一个节点(next)

适合频繁增删的场景。

🏢 企业实战理解

  • 美团:餐厅排队功能用链表实现,顾客进出队高效。

  • 滴滴:司机实时任务链基于 LinkedList 架构,支持动态增删。

 

面试题 🔟:LinkedList 为什么不支持高效随机访问?

参考答案
因为 LinkedList 是链表结构,存储位置是离散的,访问某个索引的元素需要从头/尾遍历到指定位置,时间复杂度 O(n)。

企业补充
美团外卖订单列表之前因误用 LinkedList 导致查询性能下降,后来改为 ArrayList 提高检索速度。

场景题 🔟:你在 Google Drive 实现“上传任务队列”,如何利用 LinkedList 实现生产者-消费者模型?

场景答案
LinkedList 自带双端队列能力,适合作为任务队列。生产者 addLast() 添加任务,消费者 removeFirst() 取任务处理。

示例:

uploadQueue.addLast(new UploadTask());
UploadTask task = uploadQueue.removeFirst();

Google Drive 在多线程上传中用这种结构结合锁和条件队列,实现高效任务调度。

 

5.3 迭代器源码分析:

迭代器遍历相关的三个方法:

  • Iterator<E> iterator() :获取一个迭代器对象

  • boolean hasNext() :判断当前指向的位置是否有元素

  • E next() :获取当前指向的元素并移动指针

 

🧠 理论理解

  • hasNext() 检查是否还有元素。

  • next() 获取下一个元素并移动指针。

  • remove() 安全删除当前元素。

⚠️ 内部通过游标实现状态跟踪。

🏢 企业实战理解

  • 字节跳动:自研工具链中基于 Iterator 封装安全遍历库,避免并发修改异常。

  • 腾讯:QQ群成员批量管理时封装 Iterator 支持删除操作。

  • OpenAI:数据处理模块遍历大文件数据时通过迭代器封装延迟加载机制。

面试题 1️⃣1️⃣:为什么迭代器遍历时不能用集合的 remove 方法删除元素?会抛什么异常?

参考答案
因为迭代器遍历时有“快速失败机制”(fail-fast),如果在遍历时用集合本身的方法修改,会抛出 ConcurrentModificationException。应该使用迭代器自身的 remove() 方法安全删除。

企业案例
腾讯 QQ 群批量踢人功能曾经遇到过 ConcurrentModificationException,后重构为 Iterator 迭代时删除,解决线程安全问题。

场景题 1️⃣1️⃣:你在 OpenAI 的 API 平台需要实现实时 IP 黑名单过滤,用迭代器遍历大列表时如何安全删除命中 IP?

场景答案
必须用 Iterator 的 remove() 方法 删除,否则会触发 ConcurrentModificationException

示例:

Iterator<String> it = blacklist.iterator();
while (it.hasNext()) {
    String ip = it.next();
    if (shouldRemove(ip)) {
        it.remove(); // 安全删除
    }
}

OpenAI 在 API 限流模块就利用这种方式做实时黑名单扫描,确保删除安全且不中断服务。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值