ArrayList集合类介绍

目录

一、ArrayList对象的创建

二、ArrayList的三种构造方法

2.1 ArrayList() 

2.2 ArrayList(int)

2.3 ArrayList(Collection c)

三、ArrayList常用方法介绍

3.1 boolean add(E e)

3.2 E remove(int index) 和 boolean remove(Object o)

3.3 List subList(int fromIndex, int toIndex)

3.4 常用的其他方法

四、ArrayList的遍历

4.1 sout直接输出

4.2 for循环 + 下标

4.3 foreach循环

4.4 迭代器iterator

4.5 迭代器listIterator

4.6 迭代器listIterator倒着遍历

五、ArrayList的扩容机制

六、ArrayList的优缺点

七、学习目标


在给定下标的情况下,需要经常性的访问/更新元素,采用顺序表来存储数据。

一、ArrayList<E>对象的创建

① ArrayList在java.util这个包里,使用时要导包:import java.util.ArrayList;

② ArrayList是一个泛型类,在创建对象时需要传类型。

③ ArrayList对象的创建有两种形式,如下图。

    ArrayList实现了List接口,因此可以写成方式一的形式,不过list引用能够调用的方法一定少于arrayList引用中的方法,因为ArrayList类不仅实现了List接口,而且继承和实现了其他的类和接口。

二、ArrayList<E>的三种构造方法

2.1 ArrayList() 

如果在实例化ArrayList<E>对象时调用的是不带参数的构造方法则,编译器会默认让顺序表指向一个大小为0的Object[]常量数组。

 

2.2 ArrayList(int)

如果在实例化ArrayList<E>对象时调用的是参数为int的构造方法则,如果initialCapacity的值大于0,则实例化一个大小为initialCapacity大小的Object[]类型的数组如果initialCapacity的值等于0,则编译器会让顺序表指向一个大小为0的常量Object[]数组如果initialCapacity的值小于0,则会抛出异常

2.3 ArrayList(Collection<? extends E> c)

1. 在实例化ArrayList对象时可以给构造方法传引用数据类型的对象,但是对象的类型必须实现了Collection接口,且接口的<>里得是E或E的子类,其中E表示ArrayList中<>的泛型。

2. 下图中我们将arrayList对象作为参数传递给了ArrayList<E>的构造方法,为什么不报错,而将arrayList3对象作为参数传递给了ArrayList<E>的构造方法,为什么报错?

    答:不能将ArrayList<Integer>理解成Integer的子类,arrayList传入之所以符合是因为,它的类型ArrayList<Integer>,ArrayList实现了Collection接口,而<>中的Integer也是E的子类所以可以,如果把arrayList的类型修改成ArrayList<String>,String不是E的子类,在编译器中可以看到是不可以的。

三、ArrayList<E>常用方法介绍

3.1 boolean add(E e)

了解完ArrayLisr<E>的构造方法后,我们会发现在ArrayList<E>类中如果不明确指定顺序表的大小,则编译器在构造方法初始化顺序表时会让顺序表指向一个大小为0的Object[]类型的数组,如果我们直接在实例化ArrayList<E>对象后,使用对象的引用调用add方法,会发现编译器并没有报错,可是顺序表的大小不是0吗,我们也没看到扩容操作啊,为什么不报错呢?

答:如下图所示,我们观察到add方法内部的代码,发现在给顺序表有效数据后插入新的数据前,会先判断顺序表的大小和有效数组个数size的大小是否相等,如果相等,表示需要先对顺序表进行扩容操作,具体的扩容细节见下图所示,如果是因为顺序表本身就没开辟空间,则会将顺序表的大小设置成10(此时顺序表指向的是一个新的数组),如果是因为顺序表已满,则将顺序表按照原本大小的1.5被扩容。总之,之所以没报错的原因是,add方法里存在扩容代码。

 

3.2 E remove(int index) 和 boolean remove(Object o)

E remove(int index) :删除 index 位置元素,并返回被删除的值

boolean remove(Object o):删除遇到的第一个 o

【注意】:在给remove方法传参时,如果传递的是整型,编译器只会把它认为是下标,而不会认为是你想删除顺序表中第一次出现的元素10,如果想达到删除顺序表中第一次出现的元素10,得先将10装箱,示例代码:arrayList.remove(Integer.valueOf(10))

3.3 List subList(int fromIndex, int toIndex)

List<E> subList(int fromIndex, int toIndex):截取部分 list,仍然是左闭右开(注意一下该方法的返回值为List<E>,接收subList方法的返回值时得用List<>类型的引用接收)。

【注意】:使用subList截取的结果并不是重新new了一个数组存放的截取部分,而是返回了顺序表中下标为formLindex元素的地址。如果我们修改了截取部分数组中的元素的值,顺序表中的对应部分元素的值也会同时发生改变。

3.4 常用的其他方法

public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(100);//在表尾插入元素
        arrayList.add(101);
        arrayList.add(102);
        System.out.println(arrayList);//[100, 101, 102]

        arrayList.add(1,200);//在指定下标处插入元素
        System.out.println(arrayList);//[100, 200, 101, 102]
        arrayList.add(2,300);
        System.out.println(arrayList);//[100, 200, 300, 101, 102]

        ArrayList<Integer> cur = new ArrayList<>();
        cur.add(999);
        cur.add(888);
        cur.add(999);
        arrayList.addAll(cur);//在表尾插入另一个顺序表
        System.out.println(arrayList);//[100, 200, 300, 101, 102, 999, 888, 999]

        arrayList.remove(1);//删除指定下标的元素
        System.out.println(arrayList);//[100, 300, 101, 102, 999, 888, 999]
        arrayList.remove(2);
        System.out.println(arrayList);//[100, 300, 102, 999, 888, 999]

        arrayList.remove(Integer.valueOf(999));//删除第一次出现的999
        System.out.println(arrayList);//[100, 300, 102, 888, 999]

        Integer a = arrayList.get(1);//得到某下标的元素

        arrayList.set(1,777);//把某下标的元素置为777
        System.out.println(arrayList);//[100, 777, 102, 888, 999]
        //arrayList.set(100,777);//越界异常

        boolean b = arrayList.contains(777);//是否包含某元素
        System.out.println(b);//true

        arrayList.add(777);
        System.out.println(arrayList);//[100, 777, 102, 888, 999, 777]

        int c = arrayList.indexOf(777);//某元素第一次出现的下标
        System.out.println(c);//1

        int d = arrayList.lastIndexOf(777);//从后往前找,某元素第一次出现的位置
        System.out.println(d);//5

        List<Integer> e = arrayList.subList(1,3);//
        System.out.println(e);//[777, 102]
        e.set(0,33333);
        System.out.println(arrayList);//[100, 33333, 102, 888, 999, 777]
        System.out.println(e);//[33333, 102]

        arrayList.clear();
        System.out.println(arrayList);//[]
}

四、ArrayList<E>的遍历

下面提供了5种遍历方式,使用下标遍历的好处是可以通过下标去修改顺序表中元素的值,其他3种方式(除sout)仅能得到每个元素的值,不能够修改。

4.1 sout直接输出

为什么可以用这种方法呢?因为当使用sout输出对象的引用时,打印的结果想要达到打印该对象的所有成员变量的值时,要么是ArrayList中重写了Object类的toString方法,要么它所继承类中重写了Obfect类中的toString方法,通过观察发现的确如此。

4.2 for循环 + 下标

4.3 foreach循环

4.4 迭代器iterator

【书写格式】:Iterator<xxx> name = 顺序表.iterator(); 然后通过while循环利用name引用遍历顺序表中每个元素,循环的条件是name.hasNext(),来判断是否有下一个元素,取元素的值时用name.next()取即可。

【解惑】:之所以arrayList对象可以调用iterator方法是因为,ArrayList<E>实现了Iterable<T>接口,Iterable<T>接口中有抽象方法iterator,而ArrayList<E>中重写了iterator方法。

下图是Iterable<T>接口,可以看到它有抽象方法iterator:

下图是ArrayList<E>中重写的iterator方法,可以发现它的返回类型是Iterator<E>,调用该方法后,接收返回值的时候要注意一下,要写成Iterator<E>类型。

4.5 迭代器listIterator

【书写格式】:Iterator<xxx> name = 顺序表.listIterator();

                     或 ListIterator<xxx> name = 顺序表.listIterator();

然后通过while循环,循环的条件是name.hasNext(),来判断是否有下一个元素,取元素的值时用name.next()取即可。

【注意】ListIterator<E> 是 Iterator<E>的子类,所以可以写成书写格式一的样子。

下图说明了,listIterator方法的返回值可以用ListIterator<>或Iterator<>类型的引用接收。 

4.6 迭代器listIterator倒着遍历

【说明】:listIterator方法可以传整型,而iterator方法是不可以传参的。如果给listIterator方法传整型则可以倒叙遍历顺序表,不过开始的书写格式必须写成, ListIterator<xxx> name = 顺序表.listIterator(); 不能写成Iterator<xxx> name = 顺序表.listIterator(); 因为Iterator<E>类中没有hasPrevious、previous方法。JAVA中ListIterator和Iterator详解与辨析 - Willie.wang - 博客园 (cnblogs.com)

五、ArrayList<E>的扩容机制

该扩容机制在上文的add方法中就已经讲解得差不多了,这里主要想强调的是:

① ArrayList是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容。

② 扩容时调用的时grow方法。

③ 使用Arrays.copyOf(数组,新的数组大小)进行具体的扩容

④ ArrayList<E>的扩容方法被封装了,我们无法通过ArrayList<E>对象的引用调用对应的扩容方法,它底层的扩容逻辑是按原数组的1.5倍进行扩容的。

六、ArrayList的优缺点

1. 优点:ArrayList底层使用连续的空间,在访问某一具体的元素时所用的时间复杂度为O(1)

2. 缺点:

         ① ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)。

         ② 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。增容一般是呈2或1.5倍的增长,势必会有一定的空间浪费,例如当前容量为100,满了以后增容到200,我们再继 续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

3. 针对ArrayList的缺点于是引入了链表的概念,不过链表的优缺点 其实就是 顺序表的缺优点

七、学习目标

1. 如何创建ArrayLisr对象,两种创建方法有什么区别?

2. ArrayList的三种构造方法分别做了什么事? 

3. 再次提醒,泛型类的构造方法,方法名的后面不要写<>

4. 如果顺序表通过构造方法指向的是一个大小为0的常量数组,为什么add方法可以正常使用,对顺序表的扩容/修改顺序表指向的代码在哪里实现的?

5. add()尾插,add(,)在指定位置插入,addAll()尾插另一个xx表

6. 如果给remove传int类型的值表示删除顺序表中下标为i的元素,并返回被删除的值,如果顺序表中的元素的值存入时以int类型存入的(顺序表的类型是Integer,将int类型的数据存入顺序表中时其实发生了装箱的操作),想要删除某int类型的数据,则需要先将int类型的数据装箱再传,此时调用的是remove(Object o)方法,表示删除顺序表中元素o。

7. subList可以截取顺序表的指定部分,返回的结果需要用Lis<E>类型的引用a进行接收,需要注意的是,引用a指向的不是一个新的对象,  而是指向着原顺序表中下标为formLindex元素的地址。如果我们修改了截取部分数组中的元素的值,顺序表中的对应部分元素的值也会同时发生改变。

8. ArrayList中常用的方法要记得。

9 sout、for、foreach、iterator、listIterator五种遍历顺序表的方式分别如何使用?

10. 顺序表的优缺点?

 本篇已完结 ......  

  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值