Java开发经常用到的ArrayList集合原理方法,这些你都清楚吗

前言

ArrayList 算是我们开发中最经常用到的一个集合了,使用起来很方便,对于内部元素的随机访问很快。今天来分析下ArrayList的一些原理方法,对于平常工作和面试都是非常有用的。

另外本人整理收藏了20年多家公司面试知识点整理 ,以及各种Java核心知识点免费分享给大家,我认为对面试来说是非常有用的,想要资料的话请点795983544 暗号CSDN。

在这里插入图片描述

一.ArrayList 简介

ArrayList 是一个数组队列,相当于 动态数组。与 Java 中的数组相比,它的容量能动态增长。它继承于 AbstractList,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。 看过 ArrayList 源码的同学有没有注意过有这么一个细节:为什么 ArrayList 实现了RandomAccess这个接口,但是 LinkedList 却没有实现这个接口?这是一个空接口,里面没有任何的方法,有什么作用呢?
答案: RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。也就是说,实现了这个接口的集合是支持 快速随机访问 策略的。而 LinkedList 是不能实现随机访问的。

二.ArrayList 数据结构

ArrayList 包含了两个重要的对象:elementData 和 size。

  • elementData 是"Object[]类型的数组",它保存了添加到 ArrayList 中的元素。实际上,elementData
    是个动态数组。

那是不是有人就会问既然 ArrayList 本质是数组,那为啥它的长度可以改变?

首先,数组的确长度不能改变。不过,ArrayList 内部有一系列骚操作,大概就是它每次觉得长度不够就会 创建一个新数组,这个新数组的容量比原来多出 50%,把原来的数组 copy 过来,然后把以前的数组销毁掉。

  • size 则是动态数组的实际大小。

三. ArrayList 遍历方式

  • 第 1 种,普通 for 循环随机访问,通过索引值去遍历。
// 随机访问
     List<String> list = new ArrayList<>();
     int size = list.size();
     for (int i = 0; i < size; i++) {
    
    
         value = list.get(i);
     }
  • 第 2 种,通过迭代器遍历。即通过 Iterator 去遍历。
// 迭代器遍历
    Iterator<String> iter = list.iterator();
    while (iter.hasNext()) {
    
    
        value = iter.next();
    }
  • 第 3 种,增强 for 循环遍历。
 // 增强 for 循环
    for (String s : list) {
    
    
        value = s;
    }
  • 第 4 种 forEach + lambda 循环遍历
list.forEach(p -> {
    
    
                p.hashCode();
            });

四种遍历比较

  • 结论:如果数据量比较少的话貌似四种循环耗时都差不多,但是随着数据量的增长会发现 foreach 的效率是最好的。但是从上面我们会发现一个奇怪的现象,第一次循环的时候forEach遍历的时间是最长的尽管数据量非常少也会这样。但是后面的耗时就正常了。如果放开测试里面的预热代码,每次跑出来的耗时也是正常的。

四.ArrayList 删除数据

虽然有四种遍历方式,但是能够正确删除数据的方式只有两种

  • 第 1 种通过迭代器进行删除。这种方式的话,也是《阿里代码规约》所推荐的。

在这里插入图片描述

Iterator<String> iter = list.iterator();
        while (iter.hasNext()) {
    
    
            iter.next().hashCode();
            iter.remove();
        }
  • 第 2 种倒序循环删除
for(int i = list.size()-1;i>=0;i--){
    
    
            list.remove(i);
        }

下面再演示下错误的删除操作

  • 普通 for 循环正序删除,删除过程中元素向左移动,不能删除重复的元素
 List<String> list = new ArrayList<>();
        list.add("1");
        list.add("1");
        list.add("2");
        for(int i=0;i<list.size();i++){
    
    
           list.remove(i);
        }
        System.out.println(String.join(",",list));

结果输出:1

  • 增强 for 循环删除会抛出 java.util.ConcurrentModificationException

五.ArryList 注意点

谨慎使用 ArrayList 中的 subList 方法

  • ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList. 说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。
List<String> list = new ArrayList<>();
        list.add("1");
        list.add("1");
        list.add("2");
        ArrayList<String> strings =  (ArrayList)list.subList(0, 1);

运行结果:
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList
  at com.workit.demo.listener.ArrayListTest.main(ArrayListTest.java:29)
  • 在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、 删除均会产 ConcurrentModificationException 异常。
 List<String> list = new ArrayList<>();
        list.add("1");
        list.add("1");
        list.add("2");
        List<String> subList =  list.subList(0, 1);
        // 对原 List 增加一个值
        list.add("10");
        subList.add("11"); // 这一行会报 java.util.ConcurrentModificationException
  • 初始化 List 的时候尽量指定它的容量大小。(尽量减少扩容次数)

结束语

  • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
  • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
  • 感谢您的阅读,十分欢迎并感谢您的关注。

另外本人整理收藏了20年多家公司面试知识点整理 ,以及各种Java核心知识点免费分享给大家,我认为对面试来说是非常有用的,想要资料的话请点795983544 暗号CSDN。

在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/banzhuanhu/article/details/108869680