【Java基础】Java8集合[ArrayList 常用方法讲解](源码分析+底层原理)

简介: 学习Java8集合[ArrayList 常用方法讲解](源码分析+底层原理)。

一、前言


集合主要分为Collection接口 与 Map接口两类,而ArrayList就是Collection接口中List子接口的实现类…


在上一篇文章中,我们通过ArrayList实现类的源码,了解到了其中的属性,比较印象深刻的应该是ArrayList集合底层由一个Object[]数组实现,且可以存放包括null在内的所有元素。


而在这篇文章中,我们将讲解集合中ArrayList实现类的方法:


二、集合ArrayList方法


1.构造方法


①空参构造器

源码:

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

这是源码中的空参构造器,当我们创建ArrayList对象,没有设置参数传递,也就是默认情况下,存储数据的数组elementData是一个长度为0的空数组。


List list = new ArrayList();


②带参构造器

源码:

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

当我们创建ArrayList对象,且设置了传入参数,即指定了初始大小的时候,存储数据的数组elementData初始大小就变成了我们指定的初始大小了。


List list = new ArrayList(0);


2.grow方法


源码:

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集合的底层数组,在扩容的时候会用到grow方法,我们从源码中看到newCapacity = oldCapacity + (oldCapacity >> 1);

相当于:新的容量 = 旧的容量 + 旧的容量 / 2;


所以ArrayList每次扩容都是扩容为1.5倍,然后调用Arrays类中的copyOf方法,将元素拷贝到一个新的数组当中…


3.size 方法


源码:

public int size() {
        return size;
    }

代码很简单,就是返回size的值,在上一篇文章中,我们已经熟悉过size属性:


The size of the ArrayList (the number of elements it contains).


返回的size值代表的是集合中存在的元素个数,注意:返回的不是数组的实际空间。


int count = list.size();


4.isEmpty方法


源码:

public boolean isEmpty() {
        return size == 0;
    }


此方法用于判断集合是否为空,方法中使用了上文提及的size属性:


当size == 0 时,代表集合中没有元素,返回true

当size != 0 时,代表集合中存在元素,返回false

boolean a = list.isEmpty();


5.add方法


源码:

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++;
    }
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

使用add方法时:


再插入元素之前,会检查先检查是否需要扩容,之后将传入的元素存放在数组最后一个元素后面的位置。

源码中调用了ensureCapacityInternal();方法:

当存储数据的数组elementData为空数组时,就使用默认数组长度去扩容,也就是长度默认为0,只有真正有元素传入,才会使用grow()方法创建长度为10的数组。


element放在最后元素的就没:

list.add(element);


element放在指定下标index的位置上,若位置存在元素,该元素与后续元素向后移动一位,element依旧会放置在指定的index位置上:

list.add(index,element);


6.get方法

源码:

public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }
private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    E elementData(int index) {
        return (E) elementData[index];
    }

将get方法中用到的方法也放进来了,结构不算复杂:


先是使用rangeCheck(index)方法判断是否越界;

之后就是通过传入的index下标获取对应元素;

list.get(index);


7.set方法


源码:

public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

set方法与上文提到的get方法类似:


先是使用rangeCheck(index)方法判断下标index是否越界;

然后就是将index下标对应的元素替换成传入的element;

list.set(index,element);


8.indexOf方法


源码:

public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

在indexOf方法中,我们传入一个元素,方法会返回集合中第一个等于传入元素的元素下标。

方法中运用了循环遍历集合中每个元素来查找是否存在与传入元素相同的元素…


我们还可以注意到,传入的元素包含null,再一次说明了集合可以存储包含null在内的全部元素。


int first_index = list.indexOf(null);


9.lastIndexOf方法


源码:

public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

lastIndexOf方法与上文提到的indexOf方法原理基本一致,但是遍历是从后往前的,也就是说返回的是最后一个与传入元素相同的元素下标。


int last_index = list.lastIndexOf(null);


10.remove方法


源码:

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

移除此列表中指定位置index上对应的元素。将所有后续元素向左移动一位(从它们的索引中减去1)。


list.remove(index);


到这里,集合ArrayList中较为常用的方法也就讲解完了,同时借助源码,我们还讲解了一些集合的底层实现原理,增加了对集合的认识,很棒,对吗~

微信图片_20221030164432.png




目录
相关文章
|
9月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
390 4
|
9月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
539 18
|
9月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
366 4
|
9月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
497 5
|
10月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
278 11
|
10月前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
9月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
427 1
|
9月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
394 1
|
10月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
433 0