Java编程中ArrayList源码分析

Java中的ArrayList是一种基于动态数组实现的数据结构,非常常用。相对于传统的数组,ArrayList具有更为灵活的可扩展性和易操作性。那么,在Java编程中,如何理解ArrayList的源码结构呢?接下来我将进行一些简单的分析说明。

ArrayList源码结构

概述

ArrayList类定义了Java中的动态数组,在下面的代码中可以看到其“add”、“remove”等常用方法声明:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    ...
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    ...
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // 可选清null操作,帮助GC
        return oldValue;
    }
    ...
}

成员变量

ArrayList源码中的一个重要部分就是元素数组elementData,下面给出其定义:

private static final int DEFAULT_CAPACITY = 10;

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

transient Object[] elementData;

...

其中,默认容量是10个元素,可通过构造方法实现动态扩容。此外,这里还定义了两个空数组常量,分别用于空参构造函数和初次扩容用。

构造方法

ArrayList中提供了多个构造方法,下面介绍两种常用方式:

// 使用空参构造器默认生成空数组
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 指定初始化容量
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);
    }
}

注意,空参构造函数的elementData数组是通过DEFAULTCAPACITY_EMPTY_ELEMENTDATA定义的空数组常量生成的。而在指定初始化容量的构造函数中,如果参数initialCapacity小于等于0,则抛出异常。

扩容策略

在对ArrayList进行插入操作时,若插入元素个数超出数组容量,就需要对数组进行扩容。下面是ArrayList的扩容逻辑:

private void grow(int minCapacity) {
    // 当前数组容量
    int oldCapacity = elementData.length;
    // 新的容量至少是旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果新容量还是不足目标值,则直接扩容至目标值
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果新容量超过了数组最大长度,则执行“溢出检查”
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 根据新容量创建新数组,并将旧数组内容复制到新数组中
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

如上代码所示,当ArrayList进行扩容时,容量大小至少为原来的1.5倍,如果扩容后容量依然不足,则直接扩容到目标值。若扩容后的容量超过了数组最大长度,则调用hugeCapacity方法进行溢出检查。最后,通过Arrays.copyOf方法将旧数组复制到新数组中,完成扩容操作。

示例说明

下面给出两个简单的示例说明ArrayList代码的应用:

示例1:使用ArrayList存储字符串

创建一个长度为10的ArrayList数组,然后向其中添加字符串元素:

// 新建一个长度为10的ArrayList对象
ArrayList<String> arrayList = new ArrayList<>(10);
// 向元素数组插入字符串
arrayList.add("hello");
arrayList.add("world");
arrayList.add("!");

示例2:转换数组类型(复制)

将字符串数组转换为ArrayList类型,使用Arrays.asList方法(此方法不能修改元素):

String[] arr = new String[]{"red", "green", "blue"};
ArrayList<String> arrayList = new ArrayList<>(Arrays.asList(arr));

这个示例中,使用Arrays.asList方法将字符串数组arr复制到arrayList对象中。注意,此方法返回的ArrayList对象类型不可修改 (fixed-size),但是它除了不可修改外,其他List特性都是正常的。如果不使用这种方式,可能需要逐个遍历数组中的元素,调用add方法逐个添加。

总结

Java中的ArrayList是一种基于动态数组实现的数据结构,拥有灵活的可扩展性和易操作性。通过分析ArrayList的源码结构,我们可以更深入地理解其内部实现机制,提高我们在实际编程过程中的处理效率。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java编程中ArrayList源码分析 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • servlet+JSP+mysql实现文件上传的方法

    实现文件上传功能需要前端页面、服务端servlet程序以及后台mysql数据库的支持。下面是使用servlet+JSP+mysql实现文件上传的完整攻略。 前端页面 首先,我们需要在前端页面上添加文件上传的表单,通过提交表单将文件传输到服务端。此处提供一段基本的表单代码: <form method="post" enctype=&q…

    Java 2023年6月15日
    00
  • Apache ActiveMQ任意文件写入漏洞(CVE-2016-3088)复现

    以下是Apache ActiveMQ任意文件写入漏洞(CVE-2016-3088)的完整攻略: 漏洞介绍 Apache ActiveMQ是一款开源的消息队列系统。当使用ActiveMQ的fileserver和http服务时,可以利用该漏洞将任意文件写入至任意路径,从而造成远程代码执行。 漏洞编号:CVE-2016-3088漏洞评级:高危 环境搭建 首先需要搭…

    Java 2023年6月15日
    00
  • Struts2修改上传文件大小限制方法解析

    当我们使用Struts2框架进行文件上传时,有时候会遇到上传的文件大小超过了限制的问题。默认情况下,Struts2上传文件大小限制为2M,如果需要修改文件上传大小限制,则需要进行如下操作: 步骤1:添加struts.xml配置 在struts.xml配置文件中添加以下配置,其中10485760代表文件大小限制为10M。 <interceptors&gt…

    Java 2023年5月19日
    00
  • SpringBoot热重启配置详解

    Spring Boot热重启是指在开发过程中,修改代码后无需手动重启应用程序,而是自动重新加载修改后的代码并更新应用程序。这大大提高了开发效率。下面是Spring Boot热重启的配置详解: 1. 使用Spring Boot DevTools实现热重启 Spring Boot DevTools是Spring Boot提供的一个开发工具,其中包含了热重启功能。…

    Java 2023年5月14日
    00
  • java编程之单元测试(Junit)实例分析(附实例源码)

    这里是关于“java编程之单元测试(Junit)实例分析(附实例源码)”的完整攻略。 1. 什么是单元测试? 单元测试指的是对程序中的最小代码单元进行测试,主要用来确保每一个单元都能够正常的工作。通过单元测试,我们可以确保程序的模块和功能是可靠的,同时也能够减少程序的bug数量。 2. Junit是什么? Junit是Java编程中最流行的单元测试框架之一。…

    Java 2023年5月23日
    00
  • JAVA文件读取常用工具类(8种)

    为了方便在Java中读取文件,我们通常使用Java文件读取工具类。下面是8种常用的Java文件读取工具类: BufferedReader、Scanner、InputStreamReader、FileInputStream、FileReader、LineNumberReader、RandomAccessFile和BufferedInputStream。 Buf…

    Java 2023年5月20日
    00
  • JAVA求两直线交点和三角形内外心的方法

    首先我们来介绍如何求两条直线的交点。假设我们有直线L1和直线L2,L1的解析式为y = k1x + b1,L2的解析式为y = k2x + b2。我们可以通过如下公式计算交点的坐标(x,y): $x = \frac{b2 – b1}{k1 – k2}$ $y = k1*\frac{b2 – b1}{k1 – k2} + b1$ 例如,假设L1的解析式为y =…

    Java 2023年5月19日
    00
  • MyBatis注解方式之@Update/@Delete使用详解

    MyBatis注解方式之@Update/@Delete使用详解 MyBatis提供了很多注解来使用SQL语句,其中@Update和@Delete注解可以用来更新和删除数据库中的记录。下面我们详细讲解一下这两种注解的使用方法。 @Update注解使用方法 @Update注解可以用来更新数据库中的记录。它有以下几种使用方式: 方式一:简单方式 @Update(&…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部