Java数据结构——ArrayList


前言
ArrayList是一种 顺序表,⽤⼀段 物理地址连续的存储单元依次存储数据的线性结构,一般情况都是采用数组存储,在数组上实现一些增删查改等等功能, ArrayList是Java中最常用的 动态数组实现之一,其内部的内存空间是可以改变的,今天就来探究一下ArrayList使用及其原理

在这里插入图片描述

一 ArrayList的简介

ArrayList虽然是一个普通的类,但是它实现了List接口

List接口在JDK17的部分源码
在这里插入图片描述
ArrayList类在JDK17的部分源码
在这里插入图片描述
我们可以从上面看出

1. List是一个接口,继承了Collection2. ArrayList是一个类,继承了AbstractList3. ArrayList实现了List中的RandomAccess接口,说明其支持随机访问
4. 实现了Cloneable接口,说明其支持clone
5. 实现了Serializable接口,说明其支持序列化 

下面是ArrayList的一些常用方法,因为其实现了List接口,所以List中的抽象方法,在ArrayList都有
在这里插入图片描述

二 ArrayList的构造方法

ArrayList ()无参构造
ArrayList (int initialCapacity)确定初始容量
ArrayList (Collection<? extends E> c)利用其他Collection来构造
public class Test {
    public static void main(String[] args) {
        //1.无参构造
        List<Integer> list = new ArrayList<>();
        //2.含参构造,确定其初始化的容量,确定其初始化容量为1
        List<Integer> list1 = new ArrayList<>(1);
        //3.利用其他构造
        //这里list2与list的元素一致
        List<Integer> list2 = new ArrayList<>(list);
    }
}

无参构造源码
在这里插入图片描述
在这里插入图片描述
虽然无参构造没有给定容量,但是编译器会默认一个容量,JDK17中默认容量为10

含初始容量一个参数的构造
在这里插入图片描述
如果给定初始容量,编译器会进行判断,如果初始容量>0就按照你给定的容量创建对象,如果初始容量==0就按照默认的容量进行创建,反之如果<0就抛出容量不合法的异常

注意在创建线性表的时候,要指定是什么类型的线性表
1.如果不写类型,编译器会报错,因为其不知道是什么类型
在这里插入图片描述
2.并且其必须是包装类型
如果写成普通类型编译器也会报错
在这里插入图片描述
3.那何为包装类型呢

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

这些类型中除了int和char的包装类有所改变,其他的都是基本类型的首字母大写

三 ArrayList常用方法

方法解释
boolean add(E e)尾插 e
void add(int index, E e)将 e插入到 index 位置
boolean addAll(Collection<? extends E> c)尾插 c 中的全部元素
E remove(int index)删除 index 下标元素
boolean remove(Object o)删除遇到的第一个 o
E get(int index)获取下标 index 下标元素
E set(int index, E e)将下标 index 下标元素设置为 e
void clear()清空列表
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List subList(int fromIndex, int toIndex)截取部分 list
int size()返回此列表中的实际的元素数

1.add()方法

boolean add(E e)
将指定的元素追加到此列表的末尾。

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println(list);
    }
}

运行结果如下
在这里插入图片描述

注意这里的如果已经确定了是那种包装类型,就不可以在添加其他的包装类型的数据了
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(3);
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println("扩容前长度"+list.size());
        list.add(4);
        System.out.println("扩容后长度"+list.size());
        System.out.println(list);
    }
}

运行结果如下
在这里插入图片描述
我们发现一个问题就是上面我们初始化容量为3,但是我们添加了4个元素其并没有报错,为什么呢?那是因为其ArrayList类add方法中有扩容操作,但是这里的真实长度变为4
在这里插入图片描述

void add(int index, E e)
在index下标插入e
index>=0&&index<=其列表的长度

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        //在1下标插入99
        list.add(1,99);
       // list.add(4,1);
        System.out.println(list);
    }
}

我们要注意这里并不是简单替换,而是将某一个下标修改为一个数后,原本的数要往后偏移
偏移的同时其如果存储不下,也会进行扩容

运行结果如下
在这里插入图片描述
注意这里的下标要合法 index下标>=0&&index<=list.size()
这里等于其list的容量也可以,因为这样正好就相当于上面一个参数的add进行尾插
在这里插入图片描述
如果大于list.size()就会出现数组越界异常
这里长度为3,这里index>=0&&index<=3,因此这里出现数组越界异常异常了
在这里插入图片描述

boolean addAll(Collection<? extends E> c)
将指定集合中的所有元素追加到此列表的末尾
这里是可以自己尾插自己的

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println("尾插前list"+list);
        List<Integer> list1 = new ArrayList<>();
        list1.add(10);
        list1.add(20);
        list1.add(30);
        //将list1尾插到list后面
        list.addAll(list1);
        System.out.println("尾插后list"+list);
    }
}

在这里插入图片描述
这里其实也是可以自己尾插自己的
下面这个自己尾插自己成功了
在这里插入图片描述
注意这里是不可以不同类型的列表进行尾插
一个列表中不可以放两种包装类型一样,尾插不同类型的肯定会报错
在这里插入图片描述

2.remove()方法

E remove(int index)
删除 index 下标元素
index>=0&&index<列表长度

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        //删除1下标的值
        list.remove(1);
        System.out.println(list);
    }
}

这里删除1下标的值,也就是删除这里的2
运行结果如下
在这里插入图片描述

boolean remove(Object o)
删除遇到的第一个 o
在使用的时候要将一个数装箱以后再进行删除

这里直接使用基本类型就是删除的是对应下标的值,而不是自己想要的

在这里插入图片描述
这时候就要进行装箱操作,这样就可以删除对应的值了,删除的就不是下标对应的值了
这里就是删除第一个出现的1元素,而不是下标,所以这里0下标的1被删除了
在这里插入图片描述

void clear()
清空列表

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        //删除1下标的值
        Integer a = 1;
        list.remove(a);
        System.out.println(list);
        list.clear();
        System.out.println("清空后"+list);
    }
}

这里就是将其置为空
在这里插入图片描述

3.get()和set()方法

E get(int index)
获取下标 index 下标元素

E set(int index, E e)
将下标 index 下标元素设置为 e
index>=0&&index<列表长度

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println("获取1下标的值:"+list.get(1));
        list.set(1,999);
        System.out.println("修改后1下标的值:"+list.get(1));
    }
}

这里和add方法不同的是,这个是直接修改值,而add是添加值
运行结果如下
在这里插入图片描述

4.index()方法

int indexOf(Object o) 返回第一个 o 所在下标
int lastIndexOf(Object o) 返回最后一个 o 的下标
找不到就返回-1

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(1);
        System.out.println("第一个元素1的下标:"+list.indexOf(1));//0
        //如果没有找到就返回-1
        System.out.println(list.indexOf(100));//-1
        System.out.println("最后一个元素1的下标:"+list.lastIndexOf(1));
    }
}

在这里插入图片描述

5.subList截取方法

List subList(int fromIndex, int toIndex)
截取下标[fromIndex,toIndex]的list

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        List<Integer> list1 = list.subList(1,3);
        //这里是左闭右开的截取方法
        System.out.println("截取[1,3)下标的元素:"+list1);
    }
}

运行结果如下
在这里插入图片描述
其实这里并不是真正的截取
例如:

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        List<Integer> list1 = list.subList(1,3);
        //这里是左闭右开的截取方法
        System.out.println("截取[1,3)下标的元素:"+list1);
        list1.remove(0);//删除list1中的0下标的值也就是2
        System.out.println("被截取的list: "+ list);
    }
}

这里删除截取中0下标的2,而原本的list中的2也被删除了
运行结果如下
在这里插入图片描述
可以看出这里我们对截取的内容删除,导致了list中的这部分内容也被删除了
在这里插入图片描述

只是将其一个对象指向截取的部分,只是将其截取部分的地址放入一个新对象中,因此对截取修改也会影响原本的list

四 ArrayList的遍历

for循环遍历

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        for (int i = 0; i < list.size(); i++) {
            //获取下标对应的值
            System.out.print(list.get(i)+" ");
        }
    }
}

这里的size()获取其列表长度
进行一个一个下标遍历就行
运行结果如下
在这里插入图片描述

增强for循环(for each)

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        //每个元素的类型为Integer
        for (Integer integer:list) {
            System.out.print(integer+" ");
        }
    }
}

这里使用for-each遍历,这里列表中元素类型都为Integer,就用一个临时integer来访问和打印
在这里插入图片描述

迭代器遍历

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        //使用迭代器
        //从前向后遍历
        Iterator<Integer> it = list.iterator();
        //获取其开始位置
        while(it.hasNext()){
            //不断的往后走
            System.out.print(it.next()+" ");
        }
        System.out.println();
        //从后向前遍历
        给定其下标从其最后面开始
        ListIterator<Integer> it1 = list.listIterator(list.size());
        while (it1.hasPrevious()){
            System.out.print(it1.previous()+" ");
        }
        System.out.println();
    }
}

运行结果如下
在这里插入图片描述

ArrayList问题及其思考

1.在其查找数据是非常简单和高效的
2.但是,ArrayList列表在添加或者删除元素的时候,都需要将其后面的数据向前移动,时间复杂度为O(N)
3.在添加数据时候可能会扩容,是1.5倍数扩容,有点浪费空间

因此还需要其他的数据结构来完成添加或者删除元素会更高效一点

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值