java-ArrayList与顺序表精讲+ArrayList模拟实现


前言

本文主要介绍ArrayList的构造与常用的操作方法,最后还有ArrayList的模拟实现,预祝大家学有所成。


一、ArrayList简介

在这里插入图片描述
1.ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
2.ArrayList实现了Cloneable接口,表面ArrayList是可以clone的
3.ArrayList实现了Serializable接口,表面ArrayList是支持序列化的(序列化指将一个对象转变为字符串)
4.和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList
5.ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

二、ArrayList使用

2.1ArrayList的构造

在这里插入图片描述

代码如下(示例):

public static void main(String[] args) {
        //ArrayList创建,(推荐写法)
        //构造一个空的列表
        List<Integer> list1=new ArrayList<>();//()里面放顺序表的长度

        //构造一个具有10容量的列表
        List<Integer> list2=new ArrayList<>(10);
        list2.add(1);//add方法默认插入数组最后一位
        list2.add(2);
        list2.add(3);
        //list2.add("hhh");//会报错,List<Integer已经限定了list2中只能放整形
        System.out.println(list2);//打印[1,2,3]

        //使用另一个ArrayList对list3进行初始化
        ArrayList<Integer> list3=new ArrayList<>(list2);//可以把list2的对象作为参数传给构造方法
        //需要注意的是,如果是Integer类型的ArrayList(也就是ArrayList<Integer>)
        // 如果你传参为list,那你的参数的list也必须是Integer的
        System.out.println(list3);//打印[1,2,3]
    }

2.2ArrayList常见操作

代码如下(示例):

public static void main(String[] args) {
        //基本方法介绍


        //1. add
        ArrayList<String> list=new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");//list底层是一个数组,add方法默认插入到数组最后一个位置(尾插)
        System.out.println(list);//打印[a,b,c]

        //当然了,你也可以通过传参给add方法 来实现需要位置的插入
        list.add(1,"hhh");//往下标1的地方插入字符串hhh
        System.out.println(list);//打印[a, hhh, b, c]

        //2. addAll
        //已有一个list,你可以再来一个list2整体放进list里
        ArrayList<String> list2=new ArrayList<>();
        list2.add("一个测试");
        list.addAll(list2);
        System.out.println(list);//打印[a, hhh, b, c, 一个测试],这里也是默认尾插


        //3. remove
        String ret=list.remove(1);//删除下标为1的元素,remove会返回删除的元素
        System.out.println(ret);//打印hhh
        System.out.println(list);//打印[a, b, c, 一个测试]
        //也可以删除一个具体的数据
        list.remove("a");
        System.out.println(list);//打印[b, c, 一个测试]

        //4. set
        list.set(0,"d");//把下标0的位置数据更改为d
        System.out.println(list);//打印[d, c, 一个测试]

        //5.contains
        //判断list中是否包含某个元素
        System.out.println(list.contains("c"));//打印true
        System.out.println(list.contains("a"));//打印false

        //6. indexOf
        //查找某个元素下标
        System.out.println(list.indexOf("c"));//打印1

        //7. lastIndexOf
        //查找最后一次出现数据x 的下标
        //比如我们现在list3里面有a c c c b
        ArrayList<String> list3=new ArrayList<>();
        list3.add("a");
        list3.add("c");
        list3.add("g");
        list3.add("c");
        list3.add("b");
        System.out.println(list3.lastIndexOf("c"));//打印3

        //8. subList
        //截取list中的一部分,返回一个List<E>类型的数据,如果你list是String的,那E就是String
        List<String> tmp=list3.subList(1,3);//从1下标开始截取,截取到下标3的位置,左闭右开(1下标截取,2下标截取,3下标不截取)
        System.out.println(tmp);

        

        //9. clear
        list.clear();//清空list里的数据
        System.out.println(list);
        
    }

2.3ArrayList的遍历

ArrayList可以使用三种方式进行遍历:
1.for循环+下标
2.foreach
3.使用迭代器

public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        //使用for循环+下标遍历
        //list底层是一个数组,下标从0开始
        for(int i=0;i<list.size();i++){
            System.out.print(list.get(i)+" ");
            //get(i)会返回数组下标为i的元素
            //返回值为E类型,也就是我们指定的类型,这里是Integer
        }//打印1 2 3 4
        System.out.println();

        //使用foreach遍历
        //ArrayList实现了Iterable接口,所以它可以用foreach进行打印
        //ps:用法——for(元素的类型 数组元素 :数组)
        //list底层就是一个数组
        for(Integer integer:list){
            System.out.print(integer+" ");
        }//打印1 2 3 4
        System.out.println();

        //使用迭代器进行打印
        //使用迭代器需要调用方法iterator
        Iterator<Integer> iterator=list.iterator();
        //iterator方法返回值类型为Iterator,我们用Iterator类型的接收
        //接收的都是Integer的(因为这里是Iterator<Integer> )
        while(iterator.hasNext()){
            //hasNext()方法,判断数组下面一个位置还有没有数,
            // 有就返回true,并且向数组后一位移动
            System.out.print(iterator.next()+" ");
        }//打印1 2 3 4
        System.out.println();

        //还有一种专门打印list相关的迭代器listIterator(它继承了Iterator)
        ListIterator<Integer> iterator1=list.listIterator();
        while(iterator1.hasNext()){
            System.out.print(iterator1.next()+" ");
        }//打印1 2 3 4
        //ListIterator还有add,remove(对list进行添加和删除)等非常常用的功能
    }

2.4ArrayList的扩容机制

我们来看一段代码:

public static void main(String[] args) {
        ArrayList<String> list1=new ArrayList<>();
        //初始大小为?
        ArrayList<String> list2=new ArrayList<>(20);
        //初始大小为20
    }

当我们没有传值过去,底层的数组怎么判断大小呢?我们ctrl+左键点一下ArrayList看看
在这里插入图片描述
elementData是什么?继续点进去看一下
在这里插入图片描述
发现elementData就是底层的数组名,这个数组目前是没有初始化的(没有初始化也就意味着目前没有大小)。

size是数组当前有效的数据个数

那我们在ArrayLits()那个构造方法里面看到了

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

DEFAULTCAPACITY_EMPTY_ELEMENTDATA是什么?
继续点进去看一下,发现也是一个数组
在这里插入图片描述
你会继续发现,这个冗长的玩意就是骗人的,它啥也没做。
也就是说DEFAULTCAPACITY_EMPTY_ELEMENTDATA,这个数组它初始化了,但是初始化为空(大小为0),然后我们

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

就是elementData指向了后面冗长的玩意指向的对象,但是后面指向的是个空的数组啊,所以我们的elementData也是指向一个空数组

那到这里我们就可以认为

public static void main(String[] args) {
        ArrayList<String> list1=new ArrayList<>();
        //初始大小为?
    }

list2指向的对象(数组)当前没有大小(大小为0)
简单示意图如下:
在这里插入图片描述

那这个时候问题又来了,既然这个数组大小为0,那我们存放数据的时候为什么可以成功捏?比如下面这段代码,它是没有报错的:
在这里插入图片描述
可以正常打印hhh,那我们现在就需要继续看看add是怎么实现的了,这个后面在ArrayList模拟实现中会提到。

结论
1.如果ArrayList调用了不带参数的构造方法,那么顺序表的大小为0,当第一次add的时候,整个顺序表才变为10;当这个10倍放满了,开始扩容,扩容为1.5倍。

2.如果调用的是给定容量的构造方法,那么你的顺序表大小就是给定的容量,放满了还是1.5倍进行扩容。

三、ArrayList的模拟实现

class MyArrayList<E>{
    private Object[] elementData;//底层数组
    private  int usedSize;//有效的数据个数
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA={};

    public MyArrayList(){
        this.elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//无参构造
    }

    public MyArrayList(int capacity) throws IllegalAccessException {//给了容量了
        if(capacity>0){//数组容量肯定要大于0
            this.elementData=new Object[capacity];
        }else if(capacity==0){
            this.elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }else{
            throw new IllegalAccessException("容量大小不能为负数!!!");//这里你也可以抛一个自定义异常
        }

    }

    public boolean add(E e){//添加元素,相当于存放在了顺序表最后位置
        //确定一个真正的容量
        //预测->扩容(把检查顺序表空和满以及扩容放到了一起)
        ensureCapacityInternal(usedSize+1);
        elementData[usedSize]=e;
        usedSize++;
        return true;
    }

    private void copy(int index,E e){
        for(int i=usedSize-1;i>=index;i++){
            elementData[i+1]=elementData[i];
        }
        elementData[index]=e;
    }



    public void add(int index,E e){
        //1.检查下标是否合法
        rangeCheckForAdd(index);
        //2.确定真正的容量
        ensureCapacityInternal(usedSize+1);
        //挪数据(在某个位置添加后,原先的数据要后移)
        copy(index,e);
        usedSize++;
    }
    private void rangeCheckForAdd(int index){
        if(index<0||index>size()){
            throw new IndexOutOfBoundsException("index位置不合法");
        }
    }

    //获取顺序表大小
    public int size(){
        return this.usedSize;
    }

    private void ensureCapacityInternal(int minCapacity){
        //1.计算出需要的容量
        int capacity=calculateCapacity(elementData,minCapacity);
        //2.拿着计算出的容量,去看,满了或空了就扩容
        ensureExplicitCapacity(capacity);
    }

    private void ensureExplicitCapacity(int minCapacity){//确定明确的容量
        //1.如果进不去if语句,说明数组还没有放满
        if(minCapacity-elementData.length>0){
            //扩容
            grow(minCapacity);
        }
    }

    private void grow(int minCapacity){
        int oldCapacity= elementData.length;
        int newCapacity=oldCapacity+(oldCapacity>>1);//1.5倍扩容
        if(newCapacity-minCapacity<0){
            newCapacity=minCapacity;
        }
        if(newCapacity-MAX_ARRAY_SIZE>0){
            //说明你要的容量非常大
            newCapacity=hugeCapcity(minCapacity);
        }
        elementData=Arrays.copyOf(elementData,newCapacity);
    }

    private static final int MAX_ARRAY_SIZE=Integer.MAX_VALUE-8;

    private static int hugeCapcity(int minCapacity){
        if(minCapacity<0){
            throw new OutOfMemoryError();
        }
        return (minCapacity>MAX_ARRAY_SIZE)?Integer.MAX_VALUE:MAX_ARRAY_SIZE;
    }

    private static int calculateCapacity(Object[] elementData,int minCapacity){
        //1.是否之前elementData数组分配过大小
        if(elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            return Math.max(10,minCapacity);
        }
        //2.如果分配过,就返回+1后的值
        return minCapacity;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: ArrayList是Java中的一个类,它可以动态地存储一组元素,可以根据需要动态增加或减少元素的数量,是一种非常方便的数据结构。 ArrayList的基本用法包括创建ArrayList对象、添加元素、获取元素、修改元素、删除元素等。首先,可以使用如下代码创建一个空的ArrayList对象: ```java ArrayList<String> list = new ArrayList<String>(); ``` 上面代码创建了一个类型为String的ArrayList对象,该对象初始为空。然后,可以使用add()方法向ArrayList中添加元素,例如: ```java list.add("apple"); list.add("banana"); list.add("orange"); ``` 上述代码向list中添加了三个字符串元素。可以使用get()方法获取ArrayList中的元素,例如: ```java String first = list.get(0); // 获取第一个元素 ``` 可以使用set()方法修改ArrayList中的元素,例如: ```java list.set(1, "pear"); // 将第2个元素改为"pear" ``` 可以使用remove()方法删除ArrayList中的元素,例如: ```java list.remove(2); // 删除第3个元素 ``` 以上就是ArrayList的基本用法。需要注意的是,ArrayList中的索引从0开始。 ### 回答2: ArrayList是Java中非常常用的数据结构。它提供了一个可以动态添加、删除、修改的可变长度的序列。 使用ArrayList时,首先需要引入它的包:java.util。然后可以使用如下语法创建一个ArrayList对象: ```java ArrayList<String> list = new ArrayList<String>(); ``` 这里的`<String>`说明了这个ArrayList中的元素类型是String。当然,也可以使用其他类型作为元素类型。例如: ```java ArrayList<Integer> numbers = new ArrayList<Integer>(); ArrayList<Double> prices = new ArrayList<Double>(); ``` 可以使用`add()`方法来向ArrayList中添加元素: ```java list.add("apple"); list.add("orange"); list.add("banana"); ``` 可以使用`get()`方法来获取指定位置的元素: ```java String fruit = list.get(1); //获取第二个元素,即"orange" ``` 可以使用`size()`方法来获取ArrayList中元素的个数: ```java int size = list.size(); //获取ArrayList中元素的个数 ``` 可以使用`set()`方法来修改指定位置的元素: ```java list.set(1, "pear"); //将第二个元素修改为"pear" ``` 可以使用`remove()`方法来删除指定位置的元素: ```java list.remove(2); //删除第三个元素,即"banana" ``` 需要注意的是,ArrayList中的元素是有序的,且可以重复。因此,可以使用循环来遍历ArrayList中的元素: ```java for(int i=0; i<list.size(); i++){ String fruit = list.get(i); System.out.println(fruit); } ``` 或者使用增强型循环(foreach): ```java for(String fruit : list){ System.out.println(fruit); } ``` 总之,使用ArrayList可以方便地处理可变长度的序列。在实际开发中,它有着广泛的应用场景,例如处理文件或数据库中的数据,实现算法或数据结构等。 ### 回答3: ArrayList是Java中一个非常常用的容器类。他的优点是可以存储任意类型的对象,可以动态扩容,因此在使用上非常的方便。 使用ArrayList需要在代码中首先调用import java.util.ArrayList进行导入,随后可以通过ArrayList<类型> name = new ArrayList<类型>()这个语句声明一个ArrayList,并将其命名为name,同时指定ArrayList中存储的对象类型为类型。当我们需要添加元素时,可以通过name.add(element)将元素添加到ArrayList中。我们也可以通过name.get(index)方法获取ArrayList中指定位置的元素,通过name.set(index,value)方法将ArrayList中某个位置的元素替换为新的元素。同时,我们也可以调用name.size()方法获取ArrayList中元素的数量。 值得注意的是,ArrayList中的元素是以索引的方式存储的,这意味着我们可以根据元素的位置进行添加、修改、删除等操作。而且,由于ArrayList的容量是可变的,因此其内部必须动态地管理数据的内存,这会影响到ArrayList的性能。当然,这个影响是很小的,不会对代码的运行产生显著的影响。 总之,ArrayList是Java中非常常用的容器类,其可以存储任意类型的对象,同时调用也非常方便。但在使用时需要注意其操作的复杂度,以及不能存储基本数据类型。如果需要在ArrayList中存储基本数据类型,需要借助Boxing和Unboxing机制将其转换为对应的包装类。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

劲夫学编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值