你真的知道ArrayList的使用和实现吗?

Posted 爱敲代码的三毛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你真的知道ArrayList的使用和实现吗?相关的知识,希望对你有一定的参考价值。



一、ArrayList是什么?

ArrayList 是 Java 集合框架中比较常用的数据结构,底层是一个顺序表也就是一个数组,那么它和数组最大的区别就是数组的长度是定长,而ArrayList 底层的数组是可以自动增长的。同时 ArrayList 实现了List接口。

二、通过源码了解ArrayList常用的方法

1.ArrayList的创建

首先先来了解一下 ArrayList 的创建,我们知道 ArrayList 底层是一个数组。那么它到底是怎么创建的呢?

ArrayList<Integer> arrayList = new ArrayList<>();

要想了解 ArrayList 我们就必须来看源码,

我们发现当只是 new 了一个 ArrayList 的时候,当前的数组大小是0.

2.add方法

如果只是 new 了一个 ArrayList 那么数组大小为0,也就是说只有在第一次插入元素时才为数组分配大小,且大小为10。如果是数组满了的情况下,就会进行1.5倍扩容。java集合中的扩容函数都叫做 grow;


如果在 add 的同时给了下标,如果当前下标和当前下标后有元素就会把这些元素整体整体移动到 当前 index+1 的位置。

注意:add给下标的时候不能给超过当前最后一个元素位置+2以上的位置,也就是说,如果当前数组只有3个元素你就不能给5下标。

3.addAll方法


addAll方法就是把一个元素集合整体添加进来,可以是 ArrayList 也可以是栈或者是队列。只要是实现了 Collection 接口的都可以添加进来。

代码如下(示例):

public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);

        ArrayList<Integer> list = new ArrayList<>();
        list.add(100);
        list.add(200);

        list.addAll(arrayList);
        System.out.println(list);

        Stack<Integer> stack = new Stack<>();
        stack.push(111);
        stack.push(222);
        list.addAll(stack);
        System.out.println(list);
    }

运行结果

4.remove方法


使用realme方法需要注意的是,remove方法是存在重载的,如果是 ArrayList 里面放的是整数,那么如果直接指定要删除的是数字,那么这个数字可能会被当做下标。虽然实际开发中 ArrayList 里面一般不会放整数。

代码
代码如下(示例):

public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(11);
        arrayList.add(66);
        System.out.println(arrayList);
        arrayList.remove(arrayList.get(2));//返回的是一个对象
        arrayList.remove(2);
        System.out.println(arrayList);

    }

运行结果


因为 get 返回的是一个对象所以可以通过它的返回值来删除对应的值

5.subList方法


可能有有人天真的以为这就是是一个截取嘛,但这个截取是一定要注意的。

public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add(4);
        arrayList.add(5);
        arrayList.add(6);

        List<Integer> list = arrayList.subList(1,5);
        System.out.println("修改前");
        System.out.println(arrayList);
        System.out.println(list);
        System.out.println("修改后");

        list.set(2,100);
        System.out.println(arrayList);
        System.out.println(list);
    }

运行结果


这里修改的只是截取之后的 List 里的值,但修改的同时把原来的 ArrayList里的值也修改了。所以一定要注意!

三、模拟实现 ArrayList

我简单的用泛型模拟实现了一个 ArrayList,就是为了更好理解ArrayList源码是怎么实现的。

import java.util.*;
public class MArrayList<E> {
    public E[] elem;//只是定义了一个引用
    public int usedSize;//有效的数据个数

    public MArrayList () {//初始化一个数组
        this.elem = (E[])new Object[10];
    }
    //增容判断
    public boolean isFull() {
        if(this.elem.length == this.usedSize){
            return true;
        }
        return false;
    }
    // 在 pos 位置新增元素
    public void add(int pos, E data) {
        //判断是否需要增容
        if(this.isFull()) {
            this.elem = Arrays.copyOf(this.elem,this.elem.length*2);
        }
        //判断pos位置合法性
        if (pos < 0 || pos > this.usedSize) {
            throw new ArrayIndexOutOfBoundsException("pos位置不合法");
        }
        //从后往前挪元素
        for (int i = this.usedSize; pos < i; i--) {
            this.elem[i+1] = this.elem[i];
        }
        this.elem[pos] = data;
        this.usedSize++;
    }
    // 打印顺序表
    public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.elem[i]+" ");
        }
        System.out.println();
    }

    // 判定是否包含某个元素
    public boolean contains(E toFind) {
        for (int i = 0; i < this.usedSize; i++) {
            if(this.elem[i].equals(toFind)) return true;
        }
        return false;
    }
    // 查找某个元素对应的位置
    public int search(E toFind) {
        for (int i = 0; i < this.usedSize; i++) {
            if(this.elem[i].equals(toFind)) return i;
        }
        return -1;
    }
    // 获取 pos 位置的元素
    public E getPos(int pos) {
        //判断合法性
        if(pos < 0 || pos >= this.usedSize) {
            throw new ArrayIndexOutOfBoundsException("pos位置不合法");
        }
        return this.elem[pos];
    }
    // 给 pos 位置的元素设为 value
    public void setPos(int pos, E value) {
        //判断合法性
        if(pos < 0 || pos >= this.usedSize) {
            throw new ArrayIndexOutOfBoundsException("pos位置不合法");
        }
        this.elem[pos] = value;
    }
    //删除第一次出现的关键字key
    public void remove(E key) {
        int ret = search(key);
        if(ret == -1) {
            System.out.println("关键字key不存在");
            return;
        }
        for (int i = ret; i < this.usedSize-1; i++) {
            this.elem[i] = this.elem[i+1];
        }
        this.usedSize--;
    }
    // 获取顺序表长度
    public int size() {
        return this.usedSize;
    }
    // 清空顺序表
    public void clear() {
        if (this.size() == 0) return;

        for (int i = 0; i < this.elem.length; i++) {
            this.elem[i] = null;
        }
        this.usedSize = 0;
        this.elem = null;
    }
}

总结

ArrayList刚创建大小是0,只有当添加元素时才会分配一个大小为10的数组,且往后是1.5倍数扩容。

以上是关于你真的知道ArrayList的使用和实现吗?的主要内容,如果未能解决你的问题,请参考以下文章

看得见的数据结构——关于数组表,你真的懂吗?

被迫“内卷”的程序员,前途真的一片灰暗吗?

AsyncTask你真的用对了吗?

Web前端的学习路线,你真的知道吗?

什么是后端开发?你真的知道吗?

原创Python基础:你真的知道and和or的用法吗