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日

相关文章

  • java如何实现字符串中的字母排序

    要实现字符串中字母的排序,我们可以使用Java中的字符数组和字符串操作。 步骤如下: 首先,从输入的字符串中创建一个字符数组。 然后,使用Java中提供的排序算法,对字符数组进行排序。 重新构建一个字符串,该字符串是排好序的字符数组的字符串表示形式。 以下是一个示例程序,它演示了如何在Java中实现对字符串中字母的排序: 示例1:使用冒泡排序对字符数组进行排…

    Java 2023年5月26日
    00
  • java编程小白进阶包的作用详解

    Java编程小白进阶包的作用详解 简介 Java编程小白进阶包是一个帮助Java初学者进阶的工具包,它包括了大量实用的工具类和基础知识的讲解,可以快速提升初学者的编程水平。 功能 Java编程小白进阶包的主要功能包括: 1. 工具类 Java编程小白进阶包提供了很多实用的工具类,例如字符串处理、日期时间处理、集合操作等等。这些工具类都经过了精心设计和优化,可…

    Java 2023年5月23日
    00
  • IDEA 集成log4j将SQL语句打印在控制台上的实现操作

    实现IDEA集成log4j将SQL语句打印在控制台上的操作,需要按照下面的步骤进行: 第一步:添加log4j依赖 1.在pom.xml文件中添加以下依赖: <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifa…

    Java 2023年5月26日
    00
  • 使用Ajax实现简单的带百分比进度条实例

    使用Ajax实现简单的带百分比进度条实例 在Web开发中,经常会遇到需要上传大文件或发送复杂请求的情况,此时通常会借助Ajax实现异步上传或异步请求,提高用户体验。而在这个过程中,我们通常会通过进度条来展示任务的进度情况。在本篇文章中,我们将介绍如何使用Ajax实现简单的带百分比进度条实例。 实现步骤 以下是实现带百分比进度条的基本步骤: 创建一个进度条元素…

    Java 2023年6月15日
    00
  • SpringMVC中重定向model值的获取方式

    在SpringMVC中,重定向到页面时,我们想要将一些值传递给下一个页面,这些值需要被设置在model中。下面是完整攻略: 1. 在Controller中设置重定向的model值 在Controller中设置model值并将请求重定向到另一个页面时,我们需要使用RedirectAttributes接口。可以使用addAttribute()方法将值添加到mod…

    Java 2023年6月16日
    00
  • SpringBoot应用整合ELK实现日志收集的示例代码

    ELK是一套开源的日志管理系统,由Elasticsearch、Logstash和Kibana三个组件组成。Spring Boot应用整合ELK可以实现日志收集、分析和可视化展示。以下是Spring Boot应用整合ELK实现日志收集的完整攻略: 添加依赖 在Spring Boot应用中,我们需要添加logstash-logback-encoder和sprin…

    Java 2023年5月15日
    00
  • eclipse中怎么去掉xml/js验证?

    为了去掉Eclipse中的XML和JS验证,需要按照以下步骤进行操作: 打开Eclipse,并选择菜单“Window -> Preferences” 在“Preferences”窗口中,选择“Validation”选项。 在“Validation”选项卡中,取消选中“Build automatically”复选框。 在下方的“Validators”列表…

    Java 2023年6月15日
    00
  • 启动Spring项目详细过程(小结)

    启动Spring项目详细过程 启动一个Spring项目可以分为以下几个步骤: 1. 创建项目 在IDE中创建一个新的Spring项目,可以选择使用Spring Initializr或手动创建。 使用Spring Initializr Spring Initializr是一个Web UI,可以用来方便地创建Spring项目。 打开Spring Initiali…

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