你真的知道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里的值也修改了。所以一定要注意!

6.构造方法

在这里插入图片描述

第一种构造方法就不用再说了。看看后面两种。

(1) 利用其它集合构造 ArrayList

我们可以在 new ArrayList 的同时给其他的集合作为参数 ,只要实现 了Collection 接口。

LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.add(1);
        linkedList.add(2);
        linkedList.add(3);
        ArrayList<Integer> list = new ArrayList<>(linkedList);
        list.add(100);
        System.out.println(list);

运行结果

在这里插入图片描述

(2) 给指定容量

在创建 ArrayList 的时候可以给指定容量。需要注意的是给指定容量的同时并没有发生扩容,而是直接 new 了一个指定大小的数组。

在这里插入图片描述

三、模拟实现 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倍数扩容。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的三毛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值