前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >教你如何高效使用Java中的ArrayList

教你如何高效使用Java中的ArrayList

原创
作者头像
喵手
发布2023-11-17 11:39:18
2610
发布2023-11-17 11:39:18
举报
文章被收录于专栏:Java进阶实战Java进阶实战

哈喽,各位小伙伴们,你们好呀,我是喵手。

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

  如下是Java集合体系架构图,近期几期内容都是围绕该体系进行知识讲解,以便于同学们学习Java集合篇知识能够系统化而不零散。

在这里插入图片描述
在这里插入图片描述

前言

  在Java编程中,集合是非常重要的一个概念。它是一种包装多个对象的数据结构,通常能够动态地增加或删除元素,并能够方便地访问其中的元素。其中,ArrayList就是一个非常常用的集合类,本文将对其进行详细解析。

摘要

  本文将从源代码解析、应用场景案例、优缺点分析、类代码方法介绍、测试用例等方面,深入剖析ArrayList的实现原理和使用方法,旨在帮助读者更好地理解和使用该集合类。

ArrayList集合

简介

  ArrayList是Java集合框架中的一个动态数组,它继承了AbstractList类并实现了List接口,可以存储任意类型的对象。在添加元素时,ArrayList会自动扩容,因此我们可以直接通过下标访问其中的元素。ArrayList还支持在任意位置的插入和删除操作,因此它可以非常方便地使用。

源代码解析

  ArrayList的源代码非常长,这里只对其中一些比较重要的方法进行分析。

add方法

代码语言:java
复制
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

  该方法用于向ArrayList中添加元素。首先,它会调用ensureCapacityInternal方法,该方法用于确保ArrayList内部数组的容量足够,如果不够则进行扩容。然后,它会将元素添加到数组的尾部,并返回true表示插入成功。

remove方法

代码语言:java
复制
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; // clear to let GC do its work

    return oldValue;
}

  该方法用于从ArrayList中删除指定位置的元素。首先,它会调用rangeCheck方法,该方法用于检查指定的下标是否越界。然后,它会将元素从数组中删除,并返回该元素的值。需要注意的是,删除元素后,ArrayList内部数组的大小会自动减小,并将被删元素所占据的空间赋为null,以便由垃圾回收器回收。

iterator方法

代码语言:java
复制
public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    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];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

  该方法返回一个Iterator对象,用于遍历ArrayList的元素。这里采用了内部类的形式,定义了一个Itr类,实现了Iterator<E>接口。该类维护了一个游标cursor,用于记录遍历的位置。在next方法中,它会检查是否有其他线程对ArrayList进行修改,如果有则抛出ConcurrentModificationException异常。如果没有,则返回下一个元素并将游标+1。

ensureCapacityInternal方法

代码语言:java
复制
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

  该方法用于确保ArrayList内部数组的容量足够。首先,它会调用calculateCapacity方法计算需要的最小容量。如果ArrayList内部数组为空,它会将最小容量与默认容量DEFAULT_CAPACITY进行比较,取较大值作为最终容量。否则,它直接返回最小容量。接着,它会调用ensureExplicitCapacity方法,该方法用于在必要时进行扩容。

grow方法

代码语言:java
复制
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

  该方法用于对ArrayList内部数组进行扩容。首先,它会计算出新的容量newCapacity。这里采用了位运算的方法,将原来的容量右移一位,然后与原来的容量进行相加,得到新的容量。接着,它会将新容量与最小容量进行比较,并将较大者作为新容量。如果新容量超过了MAX_ARRAY_SIZE,它会调用hugeCapacity方法进行处理。最后,它会调用Arrays.copyOf方法实现数组的扩容。

其他方法

  除了上述几个重要的方法,ArrayList还包括很多其他方法,如get、set、isEmpty、size等等,这里不一一赘述。

如下是部分源码截图,如若想学习更多,同学们可以尽情前往。

在这里插入图片描述
在这里插入图片描述

应用场景案例

  ArrayList在Java编程中的应用非常广泛,下面列举几个常见的应用场景:

  1. 需要动态地添加或删除元素,且需要支持随机访问的情况下,可以使用ArrayList。
  2. 需要对一个已知集合进行遍历或查找时,可以将该集合存储在ArrayList中。
  3. 需要对一个元素集合进行排序时,可以将其存储在ArrayList中,并使用Collections.sort方法进行排序。

优缺点分析

优点

  1. 支持动态扩容,因此可以根据需要动态地添加或删除元素。
  2. 可以支持随机访问,因此可以直接通过下标访问指定位置的元素,效率较高。
  3. ArrayList是一个线程不安全的类,因此在单线程环境下,其操作效率非常高。

缺点

  1. 删除元素时,需要将元素所在的位置之后的所有元素向前移动一位,效率较低。
  2. 插入元素时,可能需要进行数组复制和元素移动的操作,效率也较低。
  3. ArrayList是一个线程不安全的类,因此在多线程环境下,需要采取额外的措施保证线程安全。

类代码方法介绍

ArrayList类包含了很多方法,其中一些比较重要的方法介绍如下:

构造函数

代码语言:java
复制
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);
    }
}

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;
    }
}

  ArrayList类有三个构造函数,分别是无参构造函数、指定初始容量的构造函数和从其他集合类中构造的构造函数。其中,无参构造函数将elementData数组初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,即一个空数组。指定初始容量的构造函数将elementData数组初始化为指定大小的数组。从其他集合类中构造的构造函数将elementData数组初始化为其他集合类的转换结果。

add方法

代码语言:java
复制
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

  ArrayList类提供了两个方法用于添加元素,分别是在末尾添加元素和在指定位置添加元素。在末尾添加元素时,ArrayList会自动扩容,然后将元素添加到数组的末尾。在指定位置添加元素时,需要调用System.arraycopy方法将插入位置之后的元素向后移动一位,然后将元素插入到指定位置,并调整size属性的值。

remove方法

代码语言:java
复制
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; // clear to let GC do its work

    return oldValue;
}

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

  ArrayList类提供了两个方法用于删除元素,分别是按照下标删除元素和按照元素值删除元素。按照下标删除元素时,需要将删除位置之后的元素向前移动一位,并将删除位置所在的元素赋为null,最后调整size属性的值。按照元素值删除元素时,需要遍历整个数组,找到要删除的元素所在的位置,然后调用fastRemove方法进行删除。

set方法

代码语言:java
复制
public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

  ArrayList类提供了set方法用于替换指定位置的元素。该方法首先需要检查指定位置是否越界,然后将指定位置的元素替换为新的元素,并返回被替换的旧元素。

get方法

代码语言:java
复制
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

  ArrayList类提供了get方法用于获取指定位置的元素。该方法首先需要检查指定位置是否越界,然后返回指定位置的元素。

size方法

代码语言:java
复制
public int size() {
    return size;
}

  ArrayList类提供了size方法用于获取ArrayList中元素的数量,即size属性的值。

测试用例

  根据如上对ArrayList集合的理论知识进行了讲解之后,如下我们将通过写一个实测来辅助大家进行理解,到底如何使用Java中的ArrayList类。

测试代码演示

代码语言:java
复制
package com.example.javase.collection;

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

/**
 * @Author ms
 * @Date 2023-10-24 19:31
 */
public class ArrayListTest {
    public static void main(String[] args) {
        // create an empty arraylist
        List<String> list = new ArrayList<>();

        // add elements to the list
        list.add("Java");
        list.add("Python");
        list.add("C++");

        // print the list
        System.out.println("Original list: " + list);

        // insert an element at specific index
        list.add(1, "JavaScript");
        System.out.println("After insert an element: " + list);

        // get an element at specific index
        String element = list.get(2);
        System.out.println("Element at index 2: " + element);

        // remove an element at specific index
        list.remove(1);
        System.out.println("After remove an element: " + list);

        // remove an element by value
        list.remove("Java");
        System.out.println("After remove an element by value: " + list);
    }
}

  上述测试用例演示了ArrayList的基本用法,包括添加元素、插入元素、获取元素、删除元素等等。读者可以根据需要进行修改和拓展,以更好地理解和使用ArrayList。

测试代码结果

根据如上测试用例,测试结果如下:仅供参考:

在这里插入图片描述
在这里插入图片描述

测试代码分析

  根据我们如上写的测试用例代码,我们在此进行一波详细解析,大家请看:

  如上测试用例代码演示了如何使用Java中的ArrayList类。首先,创建了一个空的ArrayList对象,并通过add方法添加了三个字符串元素。然后,演示如何在指定的索引位置插入元素,获取特定索引位置的元素,以及如何删除指定索引的元素和特定值的元素。最后,通过输出语句展示了ArrayList列表的变化过程。

小结

  • ArrayList是Java集合框架中非常常用的一个动态数组,可以存储任意类型的对象。
  • ArrayList支持动态扩容和随机访问,因此在需要动态添加或删除元素,并需要支持快速随机访问的场景下,是一个非常方便的工具。
  • ArrayList的源代码非常长,但其实现原理比较简单,主要涉及到数组的扩容和元素的移动等操作。
  • ArrayList具有很高的操作效率,但在删除和插入元素时,需要进行数组元素的移动和复制,因此速度较慢。
  • ArrayList是一个线程不安全的类,因此在多线程环境下需要采取额外的措施保证线程安全。

总结

  ArrayList作为Java集合框架中的一个重要部分,在Java编程中扮演着非常重要的角色。它具有动态扩容、随机访问和丰富的 API 等特性,为开发人员提供了非常方便的数据存储和管理方式。但是,在使用ArrayList时需要注意多线程安全和操作效率的问题,在特定场景下需要选择合适的数据结构进行使用。掌握ArrayList的相关知识,对于Java开发人员来说是非常有必要的。

... ...

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

... ...

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 摘要
  • ArrayList集合
    • 简介
      • 源代码解析
        • add方法
        • remove方法
        • iterator方法
        • ensureCapacityInternal方法
        • grow方法
        • 其他方法
      • 应用场景案例
        • 优缺点分析
          • 优点
          • 缺点
        • 类代码方法介绍
          • 构造函数
          • add方法
          • remove方法
          • set方法
          • get方法
          • size方法
        • 测试用例
          • 测试代码演示
          • 测试代码结果
          • 测试代码分析
        • 小结
        • 总结
        • 文末
        相关产品与服务
        数据保险箱
        数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档