跨境互联网 跨境互联网
首页
  • AI 工具

    • 绘图提示词工具 (opens new window)
    • ChatGPT 指令 (opens new window)
  • ChatGPT

    • ChatGP T介绍
    • ChatGPT API 中文开发手册
    • ChatGPT 中文调教指南
    • ChatGPT 开源项目
  • Midjourney

    • Midjourney 文档
  • Stable Diffusion

    • Stable Diffusion 文档
  • 其他

    • AIGC 热门文章
    • 账号合租 (opens new window)
    • 有趣的网站
  • Vue

    • Vue3前置
  • JAVA基础

    • Stream
    • Git
    • Maven
    • 常用第三方类库
    • 性能调优工具
    • UML系统建模
    • 领域驱动设计
    • 敏捷开发
    • Java 测试
    • 代码规范及工具
    • Groovy 编程
  • 并发编程&多线程

    • 并发编程
    • 高性能队列 Disruptor
    • 多线程并发在电商系统下的应用
  • 其他

    • 面试题
  • 消息中间中间件

    • Kafka
    • RabbitMQ
    • RocketMQ
  • 任务调度

    • Quartz
    • XXL-Job
    • Elastic-Job
  • 源码解析

    • Mybatis 高级使用
    • Mybatis 源码剖析
    • Mybatis-Plus
    • Spring Data JPA
    • Spring 高级使用
    • Spring 源码剖析
    • SpringBoot 高级使用
    • SpringBoot 源码剖析
    • Jdk 解析
    • Tomcat 架构设计&源码剖析
    • Tomcat Web应用服务器
    • Zookeeper 高级
    • Netty
  • 微服务框架

    • 分布式原理
    • 分布式集群架构场景化解决方案
    • Dubbo 高级使用
    • Dubbo 核心源码剖析
    • Spring Cloud Gateway
    • Nacos 实战应用
    • Sentinel 实战应用
    • Seata 分布式事务
  • 数据结构和算法的深入应用
  • 存储

    • 图和Neo4j
    • MongoDB
    • TiDB
    • MySQL 优化
    • MySQL 平滑扩容实战
    • MySQL 海量数据存储与优化
    • Elasticsearch
  • 缓存

    • Redis
    • Aerospike
    • Guava Cache
    • Tair
  • 文件存储

    • 阿里云 OSS 云存储
    • FastDF 文件存储
  • 基础

    • Linux 使用
    • Nginx 使用与配置
    • OpenResty 使用
    • LVS+Keepalived 高可用部署
    • Jekins
  • 容器技术

    • Docker
    • K8S
    • K8S
  • 01.全链路(APM)
  • 02.电商终极搜索解决方案
  • 03.电商亿级数据库设计
  • 04.大屏实时计算
  • 05.分库分表的深入实战
  • 06.多维系统下单点登录
  • 07.多服务之间分布式事务
  • 08.业务幂等性技术架构体系
  • 09.高并发下的12306优化
  • 10.每秒100W请求的秒杀架构体系
  • 11.集中化日志管理平台的应用
  • 12.数据中台配置中心
  • 13.每天千万级订单的生成背后痛点及技术突破
  • 14.红包雨的架构设计及源码实现
  • 人工智能

    • Python 笔记
    • Python 工具库
    • 人工智能(AI) 笔记
    • 人工智能(AI) 项目笔记
  • 大数据

    • Flink流处理框架
  • 加密区

    • 机器学习(ML) (opens new window)
    • 深度学习(DL) (opens new window)
    • 自然语言处理(NLP) (opens new window)
AI 导航 (opens new window)

Revin

首页
  • AI 工具

    • 绘图提示词工具 (opens new window)
    • ChatGPT 指令 (opens new window)
  • ChatGPT

    • ChatGP T介绍
    • ChatGPT API 中文开发手册
    • ChatGPT 中文调教指南
    • ChatGPT 开源项目
  • Midjourney

    • Midjourney 文档
  • Stable Diffusion

    • Stable Diffusion 文档
  • 其他

    • AIGC 热门文章
    • 账号合租 (opens new window)
    • 有趣的网站
  • Vue

    • Vue3前置
  • JAVA基础

    • Stream
    • Git
    • Maven
    • 常用第三方类库
    • 性能调优工具
    • UML系统建模
    • 领域驱动设计
    • 敏捷开发
    • Java 测试
    • 代码规范及工具
    • Groovy 编程
  • 并发编程&多线程

    • 并发编程
    • 高性能队列 Disruptor
    • 多线程并发在电商系统下的应用
  • 其他

    • 面试题
  • 消息中间中间件

    • Kafka
    • RabbitMQ
    • RocketMQ
  • 任务调度

    • Quartz
    • XXL-Job
    • Elastic-Job
  • 源码解析

    • Mybatis 高级使用
    • Mybatis 源码剖析
    • Mybatis-Plus
    • Spring Data JPA
    • Spring 高级使用
    • Spring 源码剖析
    • SpringBoot 高级使用
    • SpringBoot 源码剖析
    • Jdk 解析
    • Tomcat 架构设计&源码剖析
    • Tomcat Web应用服务器
    • Zookeeper 高级
    • Netty
  • 微服务框架

    • 分布式原理
    • 分布式集群架构场景化解决方案
    • Dubbo 高级使用
    • Dubbo 核心源码剖析
    • Spring Cloud Gateway
    • Nacos 实战应用
    • Sentinel 实战应用
    • Seata 分布式事务
  • 数据结构和算法的深入应用
  • 存储

    • 图和Neo4j
    • MongoDB
    • TiDB
    • MySQL 优化
    • MySQL 平滑扩容实战
    • MySQL 海量数据存储与优化
    • Elasticsearch
  • 缓存

    • Redis
    • Aerospike
    • Guava Cache
    • Tair
  • 文件存储

    • 阿里云 OSS 云存储
    • FastDF 文件存储
  • 基础

    • Linux 使用
    • Nginx 使用与配置
    • OpenResty 使用
    • LVS+Keepalived 高可用部署
    • Jekins
  • 容器技术

    • Docker
    • K8S
    • K8S
  • 01.全链路(APM)
  • 02.电商终极搜索解决方案
  • 03.电商亿级数据库设计
  • 04.大屏实时计算
  • 05.分库分表的深入实战
  • 06.多维系统下单点登录
  • 07.多服务之间分布式事务
  • 08.业务幂等性技术架构体系
  • 09.高并发下的12306优化
  • 10.每秒100W请求的秒杀架构体系
  • 11.集中化日志管理平台的应用
  • 12.数据中台配置中心
  • 13.每天千万级订单的生成背后痛点及技术突破
  • 14.红包雨的架构设计及源码实现
  • 人工智能

    • Python 笔记
    • Python 工具库
    • 人工智能(AI) 笔记
    • 人工智能(AI) 项目笔记
  • 大数据

    • Flink流处理框架
  • 加密区

    • 机器学习(ML) (opens new window)
    • 深度学习(DL) (opens new window)
    • 自然语言处理(NLP) (opens new window)
AI 导航 (opens new window)
  • Spring Data JPA
  • MyBatis

  • Spring

  • SpringBoot

  • Jdk

    • Object类
    • ArrayList类
      • LinkedList类
      • HashMap类
      • Synchronized
      • ConcurrentHashMap
    • Tomcat

    • Netty

    • 若依

    • Traefik

    • Openresty

    • 开源框架
    • Jdk
    Revin
    2023-06-16
    目录

    ArrayList类

    # 2.1 ArrayList类结构图

    ArrayList 是一个用数组实现的集合,支持随机访问,元素有序且可以重复。

    (1)ArrayList 是一种变长的集合类,基于定长数组实现。

    (2)ArrayList 允许空值和重复元素,当往 ArrayList 中添加的元素数量大于其底层数组容量时,其会通过扩容机制重新生成一个更大的数组。

    (3)ArrayList 底层基于数组实现,所以其可以保证在 O(1) 复杂度下完成随机查找操作。

    (4)ArrayList 是非线程安全类,并发环境下,多个线程同时操作 ArrayList,会引发不可预知的异常或错误。

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
    1
    2

    image-20220214151452756

    ①、实现 RandomAccess 接口

    这是一个标记接口,一般此标记接口用于 List实现,以表明它们支持快速(通常是恒定时间)的随机访问

    ②、实现 Cloneable 接口

    Cloneable 和 RandomAccess 接口一样也是一个标记接口,接口内无任何方法体和常量的声明,也就是说如果想克隆对象,必须要 实现 Cloneable 接口,表明该类是可以被克隆的。

    ③、实现 Serializable 接口

    标记接口,表示能被序列化

    ④、实现 List 接口

    ​ 这个接口是 List 类集合的上层接口,定义了实现该接口的类都必须要实现的一组方法

    image-20220214152521279

    # 2.2 字段属性

            //集合的默认大小
            private static final int DEFAULT_CAPACITY = 10;
            //空的数组实例
            private static final Object[] EMPTY_ELEMENTDATA = {};
            //这也是一个空的数组实例,和EMPTY_ELEMENTDATA空数组相比是用于了解添加元素时数组膨胀多少
            private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
            //存储 ArrayList集合的元素,集合的长度即这个数组的长度
            //1、当 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时将会清空 ArrayList
            //2、当添加第一个元素时,elementData 长度会扩展为 DEFAULT_CAPACITY=10
            transient Object[] elementData;
            //表示集合的长度
            private int size;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    # 2.3 类构造器

    1. 无参构造:

         public ArrayList() {
             this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
         }
    
    1
    2
    3

    此无参构造函数将创建一个 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 声明的数组,注意此时初始容量是0,而不是大家以为的 10。

    注意:根据默认构造函数创建的集合,ArrayList list = new ArrayList();此时集合长度是0.

    2. 重载:有参构造ArrayList(int initialCapacity)

    public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    初始化集合大小创建 ArrayList 集合。当大于0时,给定多少那就创建多大的数组;当等于0时,创建一个空数组;当小于0时,抛出异常。

    3.重载:ArrayList(Collection<? extends E> c)

    public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    将已有的集合复制到 ArrayList 集合中

    思考:无参构造和0长度构造有什么区别?

     //两种方式构建list,有什么区别?
     ArrayList list1 = new ArrayList();
     ArrayList list2 = new ArrayList(0);
    
    1
    2
    3
       @Test
        public void test2(){
            //两种方式构建list,有什么区别?
            ArrayList list1 = new ArrayList();
            ArrayList list2 = new ArrayList(0);
    
    
            //打印对象头
            System.out.println(ClassLayout.parseInstance(list1).toPrintable());
            System.out.println(ClassLayout.parseInstance(list2).toPrintable());
    
    
            System.out.println("========");
    
    
            //add一个元素之后再来打印试试
            list1.add(1);
            list2.add(1);
    
    
            System.out.println(ClassLayout.parseInstance(list1).toPrintable());
            System.out.println(ClassLayout.parseInstance(list2).toPrintable());
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    image-20220228161636767

    原理:

            //calculateCapacity
            //每次元素变动,比如add,会调用该函数判断容量情况
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            //定义default empty数组的意义就在这里!用于扩容时判断当初采用的是哪种构造函数
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                //如果是无参的构造函数,用的就是该default empty
                //那么第一次add时候,容量取default和min中较大者
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            //如果是另外两个构造函数,比如指定容量为5,或者初始参数collection为5
            //那就直接返回5,一定程度上,节约了内存空间
            return minCapacity;
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 2.4 添加元素

    // 思考:List集合底层是数组,为什么能添加到任意多个元素?
    list1.add(1);
    
    1
    2

    源码:

      public boolean add(E e) {
            ensureCapacityInternal(size + 1);  //添加元素之前,首先要确定集合的大小(是否需要扩容)
            elementData[size++] = e;
            return true;
        }
    
    1
    2
    3
    4
    5

    如上所示,在通过调用 add 方法添加元素之前,我们要首先调用 ensureCapacityInternal 方法来确定集合的大小,如果集合满了,则要进行扩容操作。

    private void ensureCapacityInternal(int minCapacity) {//这里的minCapacity 是集合当前大小+1
            //elementData 是实际用来存储元素的数组,注意数组的大小和集合的大小不是相等的,前面的size是指集合大小
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果数组为空,则从size+1的值和默认值10中取最大的
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;//不为空,则返回size+1
        }
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
    
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    在 ensureExplicitCapacity 方法中,首先对修改次数modCount加一,这里的modCount给ArrayList的迭代器使用的,在并发操作被修改时,提供快速失败行为(保证modCount在迭代期间不变,否则抛出ConcurrentModificationException异常,可以查看源码865行),接着判断minCapacity是否大于当前ArrayList内部数组长度,大于的话调用grow方法对内部数组elementData扩容,grow方法代码如下:

    private void grow(int minCapacity) {
            int oldCapacity = elementData.length;//得到原始数组的长度
            int newCapacity = oldCapacity + (oldCapacity >> 1);//新数组的长度等于原数组长度的1.5倍
            if (newCapacity - minCapacity < 0)//当新数组长度仍然比minCapacity小,则为保证最小长度,新数组等于minCapacity
                newCapacity = minCapacity;
            //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 = 2147483639
            if (newCapacity - MAX_ARRAY_SIZE > 0)//当得到的新数组长度比 MAX_ARRAY_SIZE 大时,调用 hugeCapacity 处理大数组
                newCapacity = hugeCapacity(minCapacity);
            //调用 Arrays.copyOf 将原数组拷贝到一个大小为newCapacity的新数组(注意是拷贝引用)
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    
    
        private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0) //
                throw new OutOfMemoryError();
            return (minCapacity > MAX_ARRAY_SIZE) ? //minCapacity > MAX_ARRAY_SIZE,则新数组大小为Integer.MAX_VALUE
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    扩容的核心方法就是调用前面我们讲过的Arrays.copyOf 方法,创建一个更大的数组,然后将原数组元素拷贝过去即可

    对于 ArrayList 集合添加元素,总结一下:

    ①、当通过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。

    ②、第 2 次添加元素,集合不为空,而且由于集合的长度size+1是小于数组的长度10,所以直接添加元素到数组的第二个位置,不用扩容。

    ③、第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候创建一个长度为10+10*0.5 = 15 的数组(扩容1.5倍),然后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。

    ④、第 Integer.MAX_VALUE - 8 = 2147483639,然后 2147483639%1.5=1431655759(这个数是要进行扩容) 次添加元素,为了防止溢出,此时会直接创建一个 1431655759+1 大小的数组,这样一直,每次添加一个元素,都只扩大一个范围。

    ⑤、第 Integer.MAX_VALUE - 7 次添加元素时,创建一个大小为 Integer.MAX_VALUE 的数组,在进行元素添加。

    ⑥、第 Integer.MAX_VALUE + 1 次添加元素时,抛出 OutOfMemoryError 异常。

    注意:能向集合中添加 null 的,因为数组可以有 null 值存在。

    Object[] obj = {null,1};
    
    
    ArrayList list = new ArrayList();
    list.add(null);
    list.add(1);
    System.out.println(list.size());//2
    
    1
    2
    3
    4
    5
    6
    7

    # 2.5 删除元素

    public E remove(int index) {
            rangeCheck(index);//判断给定索引的范围,超过集合大小则抛出异常
    
    
            modCount++;
            E oldValue = elementData(index);//得到索引处的删除元素
    
    
            int numMoved = size - index - 1;
            if (numMoved > 0)//size-index-1 > 0 表示 0<= index < (size-1),即索引不是最后一个元素
                //通过 System.arraycopy()将数组elementData 的下标index+1之后长度为 numMoved的元素拷贝到从index开始的位置
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; //将数组最后一个元素置为 null,便于垃圾回收
    
    
            return oldValue;
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    remove(int index) 方法表示删除索引index处的元素,首先通过 rangeCheck(index) 方法判断给定索引的范围,超过集合大小则抛出异常;接着通过 System.arraycopy 方法对数组进行自身拷贝

    image-20220214161850004

    附:

    /*
    * src:源数组
      srcPos:源数组要复制的起始位置
      dest:目的数组
      destPos:目的数组放置的起始位置
      length:复制的长度
      注意:src 和 dest都必须是同类型或者可以进行转换类型的数组。
    */
    public static native void arraycopy(Object src,  int  srcPos,
                                            Object dest, int destPos,
                                            int length);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    # 2.6 修改元素

    通过调用 set(int index, E element) 方法在指定索引 index 处的元素替换为 element。并返回原数组的元素。

    public E set(int index, E element) {
            rangeCheck(index);//判断索引合法性
    
    
            E oldValue = elementData(index);//获得原数组指定索引的元素
            elementData[index] = element;//将指定所引处的元素替换为 element
            return oldValue;//返回原数组索引元素
      }
    
    1
    2
    3
    4
    5
    6
    7
    8

    通过调用 rangeCheck(index) 来检查索引合法性

        private void rangeCheck(int index) {
            if (index >= size)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    
    1
    2
    3
    4

    当索引为负数时,会抛出 java.lang.ArrayIndexOutOfBoundsException 异常。当索引大于集合长度时,会抛出 IndexOutOfBoundsException 异常。

    # 2.7 查找元素

    public E get(int index) {
            rangeCheck(index);
    
    
            return elementData(index);
        }
    
    1
    2
    3
    4
    5
    6

    同理,首先还是判断给定索引的合理性,然后直接返回处于该下标位置的数组元素。

    # 2.8 遍历集合

    # ①、普通 for 循环遍历

    前面我们介绍查找元素时,知道可以通过get(int index)方法,根据索引查找元素,那么遍历同理:

    ArrayList list = new ArrayList();
    list.add("a");
    list.add("b");
    list.add("c");
    for(int i = 0 ; i < list.size() ; i++){
        System.out.print(list.get(i)+" ");
    }
    
    1
    2
    3
    4
    5
    6
    7

    # ②、迭代器 iterator

    ArrayList<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    Iterator<String> it = list.iterator();
    while(it.hasNext()){
        String str = it.next();
        System.out.print(str+" ");
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    在介绍 ArrayList 时,我们知道该类实现了 List 接口,而 List 接口又继承了 Collection 接口,Collection 接口又继承了 Iterable 接口,该接口有个 Iterator iterator() 方法,能获取 Iterator 对象,能用该对象进行集合遍历,为什么能用该对象进行集合遍历?我们再看看 ArrayList 类中的该方法实现:

      public Iterator<E> iterator() {
             return new Itr();
      }
    
    1
    2
    3

    该方法是返回一个 Itr 对象,这个类是 ArrayList 的内部类。

    private class Itr implements Iterator<E> {
            int cursor;       //游标, 下一个要返回的元素的索引
            int lastRet = -1; // 返回最后一个元素的索引; 如果没有这样的话返回-1.
            int expectedModCount = modCount;
    
    
            //通过 cursor != size 判断是否还有下一个元素
            public boolean hasNext() {
                return cursor != size;
            }
    
    
            @SuppressWarnings("unchecked")
            public E next() {
                checkForComodification();//迭代器进行元素迭代时同时进行增加和删除操作,会抛出异常
                int i = cursor;
                if (i >= size)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i + 1;//游标向后移动一位
                return (E) elementData[lastRet = i];//返回索引为i处的元素,并将 lastRet赋值为i
            }
    
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
    
                try {
                    ArrayList.this.remove(lastRet);//调用ArrayList的remove方法删除元素
                    cursor = lastRet;//游标指向删除元素的位置,本来是lastRet+1的,这里删除一个元素,然后游标就不变了
                    lastRet = -1;//lastRet恢复默认值-1
                    expectedModCount = modCount;//expectedModCount值和modCount同步,因为进行add和remove操作,modCount会加1
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
    
            @Override
            @SuppressWarnings("unchecked")
            public void forEachRemaining(Consumer<? super E> consumer) {//便于进行forEach循环
                Objects.requireNonNull(consumer);
                final int size = ArrayList.this.size;
                int i = cursor;
                if (i >= size) {
                    return;
                }
                final Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length) {
                    throw new ConcurrentModificationException();
                }
                while (i != size && modCount == expectedModCount) {
                    consumer.accept((E) elementData[i++]);
                }
                // update once at end of iteration to reduce heap write traffic
                cursor = i;
                lastRet = i - 1;
                checkForComodification();
            }
    
    
            //前面在新增元素add() 和 删除元素 remove() 时,我们可以看到 modCount++。修改set() 是没有的
            //也就是说不能在迭代器进行元素迭代时进行增加和删除操作,否则抛出异常
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73

    注意在进行 next() 方法调用的时候,会进行 checkForComodification() 调用,该方法表示迭代器进行元素迭代时,如果同时进行增加和删除操作,会抛出 ConcurrentModificationException 异常。比如:

    ArrayList<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    Iterator<String> it = list.iterator();
    while(it.hasNext()){
        String str = it.next();
        System.out.print(str+" ");
        list.remove(str);//集合遍历时进行删除或者新增操作,都会抛出 ConcurrentModificationException 异常
        //list.add(str);
        list.set(0, str);//修改操作不会造成异常
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    image-20220214165507151

    解决办法是不调用 ArrayList.remove() 方法,转而调用 迭代器的 remove() 方法:

    Iterator<String> it = list.iterator();
    while(it.hasNext()){
        String str = it.next();
        System.out.print(str+" ");
        //list.remove(str);//集合遍历时进行删除或者新增操作,都会抛出 ConcurrentModificationException 异常
        it.remove();
    }
    
    1
    2
    3
    4
    5
    6
    7

    注意:迭代器只能向后遍历,不能向前遍历,能够删除元素,但是不能新增元素。

    # ③、迭代器的变种 forEach

    ArrayList<String> list = new ArrayList<>();
     list.add("a");
     list.add("b");
     list.add("c");
    for(String str : list){
        System.out.print(str + " ");
    }
    
    1
    2
    3
    4
    5
    6
    7

    这种语法可以看成是 JDK 的一种语法糖,通过反编译 class 文件,我们可以看到生成的 java 文件,其具体实现还是通过调用 Iterator 迭代器进行遍历的。如下:

    ArrayList list = new ArrayList();
            list.add("a");
            list.add("b");
            list.add("c");
            String str;
            for (Iterator iterator1 = list.iterator(); iterator1.hasNext(); System.out.print((new StringBuilder(String.valueOf(str))).append(" ").toString()))
                str = (String)iterator1.next();
    
    1
    2
    3
    4
    5
    6
    7

    总结:

    • arrayList可以存放null。
    • arrayList本质上就是一个elementData数组。
    • arrayList区别于数组的地方在于能够自动扩展大小,其中关键的方法就是gorw()方法。
    • arrayList由于本质是数组,所以它在数据的查询方面会很快,而在插入删除这些方面,性能下降很多,有移动很多数据才能达到应有的效果
    上次更新: 2025/04/03, 11:07:08
    Object类
    LinkedList类

    ← Object类 LinkedList类→

    最近更新
    01
    tailwindcss
    03-26
    02
    PaddleSpeech
    02-18
    03
    whisper
    02-18
    更多文章>
    Theme by Vdoing | Copyright © 2019-2025 跨境互联网 | 豫ICP备14016603号-5 | 豫公网安备41090002410995号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式