• ArrayList与顺序表(2)


     前言~🥳🎉🎉🎉  

    hellohello~,大家好💕💕,这里是E绵绵呀✋✋ ,如果觉得这篇文章还不错的话还请点赞❤️❤️收藏💞 💞 关注💥💥,如果发现这篇文章有问题的话,欢迎各位评论留言指正,大家一起加油!一起chin up!👍👍 

    💥个人主页:E绵绵的博客
    💥所属专栏:JAVA知识点专栏   JAVA题目练习  c语言知识点专栏   c语言题目练习

    在上一章我们将顺序表的模拟讲了之后,我们现在正式开始介绍ArrayList这个类(顺序表)。开始吧! 


    参考文章:【Java 数据结构】顺序表_数据结构java顺序表基本算法测试-CSDN博客

    ArrayList的介绍 

     在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:

    ❤️❤️由这可知:

    1. ArrayList是以泛型方式实现的,所以使用时必须要先实例化

    2. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问

    3. ArrayList实现了Cloneable接口,表明ArrayList是可以clone的

    4. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的

    5. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者 CopyOnWriteArrayList

    6. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表


    我们查看源码可知的确实施了这些接口。

    这就是类定义的前部分,这里还是比较复杂的,会随着我们学习的深入,逐步学习到。

    ❤️❤️接下来我们来看ArrayList的几个成员变量:



    对于第一个成员变量,我们无需了解,只需要关注下后面五个成员变量。

     使用ArrayList


     ArrayList的构造方法 


     第一个构造方法

    当前是一个带参数的构造方法,很好理解,根据传递的参数开辟数组空间的大小。如果参数是等于0,就直接把 EMPTY_ELEMENTDATA 这个空数组赋值给存放数据的数组中。 如果是给定一个负数,显然是错误的,也即直接抛出异常!

    第二个构造方法 


    对于这个无参构造方法居然也是给了一个空数组,也就是没有分配数组内存,那它到底是怎么把数据放进去的?别急,随着后面的讲解,我们会解开这个谜题。

    第三个构造方法 

     


    里面涉及了泛型的进阶,我们这也不怎么好描述,就直接说特点吧:

    下面是这个构造方法的一些特点:

    参数c要求是实现了Collection接口的对象,

    参数c中的<>元素类型必须与ArrayList中的<>元素类型兼容,即参数c中的<>元素类型必须是ArrayList中<>元素类型的子类或者相同类型。

    在使用该构造方法后,它会按照参数c中元素的顺序将元素添加到新创建的ArrayList中。

    实例如下:

    1. public class Test {
    2. public static void main(String[] args) {
    3. List<String> list = new ArrayList<>();
    4. list.add("apple");
    5. list.add("banana");
    6. list.add("orange");
    7. ArrayList<String> arrayList = new ArrayList<>(list);
    8. System.out.println(arrayList);
    9. }
    10. }


    所以由这可知我们就将list内部的元素apple banana orange全部复制到Arraylist内部中(按顺序复制)。

    ArrayList中的tostring方法 

    由上图可知我们的ArrayList重写了toString方法。所以在用println时,内部参数为ArrayList对象时其将打印出该对象内部的所有元素,以字符串形式打印出来。

    例如,如果ArrayList中有三个元素 “apple”、“banana” 和 “orange”,那么调用println方法将打印出字符串 “[apple, banana, orange]”。

    ArrayList 的 add 方法 


    别小看这几行代码,跟我们自己模拟实现的还是有区别的,真正有内涵的代码其实在 ensureCapacityInternal 这个方法中,那么现在,我们就一步步去解开他的面纱:

    由这可知,我们就可以解开之前构造方法中的问题:我们用无参构造方法给其elementData一个空数组,也就是没有分配内存,那么它到底怎么存放数据进去?


    我们看源码可知如果在使用add时elementData指向一个空数组,那么在使用add方法时内部的ensureCapacityInternal方法会给其element重新指向一个可以存放十个数据的数组,这样就可以存放数据了。


    我们还从源码处可知在使用add时如果其数组中的数据已经满了,那么其会为该数组扩容1.5倍再存放数据。


    所以这就是add的厉害之处。不仅能给内部为空数组的ArrayList对象重新开辟一个内部为10个数据的数组,使其能存放数据; 它还可以为满数据的数组实现扩容,使其也能存放数据。

     ArrayList的常用方法


    在这里我们就重点讲两个方法 addAll和subList,

    ❤️❤️对于其他方法的使用,都很简单,自己去查源码,我这就不讲了。到了数据结构阶段,就要尝试着自己看源码,培养自主学习的能力!

     addAll方法

    ArrayList中的addAll方法是用于将另一个集合c中的所有元素添加到当前ArrayList对象中的方法。它的语法如下:

    boolean addAll(Collection<? extends E> c)

    其中有以下要求:

    参数c要求是实现了Collection接口的对象,

    参数c中的<>元素类型必须与ArrayList中的<>元素类型兼容,即参数c中的<>元素类型必须是ArrayList中<>元素类型的子类或者相同类型。


    这个方法可以用于批量添加元素到ArrayList对象中,方便快捷。注意,addAll方法不会去重,如果添加的元素在当前ArrayList中已经存在,则会重复添加

     subList方法

    ArrayList中的subList方法用于获取原顺序表的一个子顺序表。它接受两个参数,分别是起始索引(fromindex)和结束索引(toindex),其左闭右开。一个新的List对象,包含原顺序表中指定范围内的元素。


    subList方法返回的子顺序表在原顺序表的内部,对子顺序表的修改会反映到原顺序表上,反之亦然。这意味着,如果你修改了子顺序表中的元素,原顺序表也会相应地被修改;如果你修改了原顺序表中的元素,子顺序表也会相应地被修改。


    需要注意的是,如果有一个顺序表此时存在一个子顺序表,现在将该顺序表结构性修改(如添加或删除元素),之后再用println打印子顺序表,会导致ConcurrentModificationException异常抛出。这是因为结构性修改改变了原顺序表的大小,从而也破坏了其子列表的大小,所以打印子列表时就报错。(内容牵涉到了迭代器,这里就不细讲)


    1. ArrayList<Integer> list = new ArrayList<>();
    2. list.add(1);
    3. list.add(2);
    4. list.add(3);
    5. list.add(4);
    6. list.add(5);
    7. List<Integer> subList = list.subList(1, 4);
    8. System.out.println(subList); // 输出:[2, 3, 4]
    9. subList.set(0, 10);
    10. System.out.println(list); // 输出:[1, 10, 3, 4, 5]
    11. list.add(6);
    12. System.out.println(subList); // 抛出ConcurrentModificationException异常

    ArrayList的常用方法总使用 

     ❤️❤️以下是我们对常用方法的总使用,大家了解一下。

    1. ArrayList<Integer> arrayList = new ArrayList<>();
    2. // 1,插入(尾插)
    3. arrayList.add(1);
    4. arrayList.add(2);
    5. arrayList.add(3);
    6. arrayList.add(4);
    7. arrayList.add(5);
    8. System.out.println("插入数据后:" + arrayList);
    9. // 2,在任意位置插(下标)插入
    10. arrayList.add(0, -1);
    11. System.out.println("在0下标插入-1后:" + arrayList);
    12. // new 一个链表对象并尾插“1”,“2
    13. LinkedList<Integer> linkedList = new LinkedList<>();
    14. linkedList.addLast(1);
    15. linkedList.addLast(2);
    16. // 3,插入 linklist 的所有元素
    17. arrayList.addAll(linkedList);
    18. System.out.println("插入linklist后:" + arrayList);
    19. // 4,删除任意位置(下标)数据
    20. arrayList.remove(0);
    21. System.out.println("删除0下标数据后:" + arrayList);
    22. // 5,删除指定数据
    23. arrayList.remove(new Integer(1));// 参数为Object(类)类型的对象
    24. System.out.println("删除第一个1后:" + arrayList);
    25. // 上面两个remove()方法构成了重载
    26. // remove()方法的参数如果输入整数之后不会自动装箱,而是会被自动识别为index
    27. // 因为index是int类型,而顺序表中的数据是Object(类)类型
    28. // 但在插入数据时,输入的是1,基本类型,它会自动装箱,变成Integer类型
    29. //删除数据时之所以输入整数时不自动装箱是因为其参数本身就有index(整数)这个类型
    30. //所以优先选择该整型参数,不会自动装箱。
    31. // 所以要删除数据,应该输入类类型的对象,而不是基本类型的数据
    32. // 6,获取任意位置(下标)数据
    33. int ret = arrayList.get(0);
    34. System.out.println("得到0下标的数据:" + ret);
    35. // 7,更改任意位置(下标)数据
    36. arrayList.set(0,100);
    37. System.out.println("把0下标数据改成100后:" + arrayList);
    38. // 8,判断是否存在该数据
    39. boolean bl = arrayList.contains(100);
    40. System.out.println("判断是否存在100这个数据:" + bl);
    41. // 9,返回第一个key的位置(下标)
    42. int index = arrayList.indexOf(2);
    43. System.out.println("第一个2的下标:" + index);
    44. // 10,返回最后一个key的位置(下标)
    45. int lastIndex = arrayList.lastIndexOf(2);
    46. System.out.println("最后一个2的下标:" + lastIndex);
    47. // 11,获取顺序表长度
    48. int size = arrayList.size();
    49. System.out.println("顺序表长度为:" + size);
    50. // 12,截取
    51. List<Integer> list = arrayList.subList(1, 3);
    52. // [1,3)左闭右开,返回出的子顺序表和ArrayList共用一个elementData数组
    53. System.out.println(list);
    54. // 13,清空顺序表
    55. arrayList.clear();
    56. System.out.println("清空顺序表后:" + arrayList);

    ArrayList的遍历 

     ❤️❤️对于顺序表的遍历,我们可以通过 for 循环,for-each,以及迭代器的方法遍历:

    1. public class TestArrayList {
    2. public static void main(String[] args) {
    3. ArrayList<Integer> arrayList = new ArrayList<>();
    4. arrayList.add(1);
    5. arrayList.add(2);
    6. arrayList.add(3);
    7. // 通过for循环遍历ArrayList
    8. for (int i = 0; i < arrayList.size(); i++) {
    9. System.out.print(arrayList.get(i) + " ");
    10. }
    11. // 通过for-each循环遍历ArrayList
    12. for (Integer integer : arrayList) {
    13. System.out.print(integer + " ");
    14. }
    15. // 通过迭代器遍历ArrayList(了解即可,无需深入了解,之后会学)
    16. Iterator<Integer> it = arrayList.iterator();
    17. while (it.hasNext()) {
    18. System.out.print(it.next() + " ");
    19. }
    20. }
    21. }

    除此以外,我们还可以通过println(ArrayList对象)打印出该对象内部的所有元素,这个方法我们在之前就讲过了,这里不多讲述了。

     总结

    所以对于我们的顺序表的相关知识点就讲完啦,之后将给大家介绍链表,敬请期待! 还希望各位大佬们能给个三连,点点关注,点点赞,发发评论呀,感谢各位大佬~❤️❤️💕💕🥳🎉🎉🎉

     

     

     

  • 相关阅读:
    【LeetCode】1422. 分割字符串的最大得分
    Java中DateTimeFormatter的使用方法和案例
    『 基础算法题解 』之双指针(上)
    高可用(keepalived)部署方案
    一:Spring源码解析之prepareRefresh()
    Git本地仓库的文件夹不显示红色感叹号、绿色对号等图标
    恒创科技:无法与服务器建立安全连接怎么解决?
    软件质量保护与测试(第2版)学习总结第十章 黑盒测试
    分布式:Docker
    Mybatis(四)
  • 原文地址:https://blog.csdn.net/Easonmax/article/details/138090798