【Java从入门到放弃 之 ArrayList】

列表

ArrayList

ArrayList 是 Java 集合框架中的一部分,实现了 List 接口。它提供了一个动态数组,可以自动调整大小以容纳更多的元素。ArrayList 是基于数组实现的,因此它提供了快速的随机访问性能,但在插入和删除操作上可能不如链表结构高效(特别是当这些操作发生在列表中间时)。

ArrayList 的详细介绍:

  1. 概述
    定义:ArrayList 是一个可以动态增长和缩小的数组实现,允许存储任意数量的对象。
    特点:
    线程不安全:ArrayList 不是同步的,如果多个线程同时修改同一个 ArrayList 实例,则必须显式地进行同步。
    允许重复元素:与 Set 不同,ArrayList 允许存储重复的元素。
    允许空值:支持存储 null 值,甚至可以存储多个 null。
  2. 内部工作原理
    ArrayList 内部使用一个对象数组来存储元素。当添加新元素时,如果当前数组容量不足以容纳新元素,ArrayList 会创建一个新的、更大的数组,并将所有现有元素复制到新的数组中。默认情况下,每次扩容大约增加原来容量的一半左右(具体取决于 JVM 实现)。
java 复制代码
        private transient Object[] elementData;
        private int size;

基本用法

java 复制代码
        public boolean add(E e) //添加元素到末尾
        public boolean isEmpty() //判断是否为空
        public int size() //获取长度
        public E get(int index) //访问指定位置的元素
        public int indexOf(Object o) //查找元素, 如果找到,返回索引位置,否则返回-1
        public int lastIndexOf(Object o) //从后往前找
        public boolean contains(Object o) //是否包含指定元素,依据是equals方法的返回值
        public E remove(int index) //删除指定位置的元素, 返回值为被删对象
        //删除指定对象,只删除第一个相同的对象,返回值表示是否删除了元素
        //如果o为null,则删除值为null的元素
        public boolean remove(Object o)
        public void clear() //删除所有元素
        //在指定位置插入元素,index为0表示插入最前面,index为ArrayList的长度表示插到最后面
        public void add(int index, E element)
        public E set(int index, E element) //修改指定位置的元素内容

add 方法

java 复制代码
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
java 复制代码
    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

add的方法比较简单,里面有一个modCount代表修改次数,首先modCount++;然后调用add(E e, Object[] elementData, int s)这个方法;这个方法首先判断有没有到数组边界,如果到了数组边界,说明要进行扩容了;就调用grow()方法;如果没有到边界就直接在数组后面增加一个新的元素。

remove方法

java 复制代码
    public E remove(int index) {
        Objects.checkIndex(index, size);
        final Object[] es = elementData;

        @SuppressWarnings("unchecked") E oldValue = (E) es[index];
        fastRemove(es, index);

        return oldValue;
    }
java 复制代码
    private void fastRemove(Object[] es, int i) {
        modCount++;
        final int newSize;
        if ((newSize = size - 1) > i)
            System.arraycopy(es, i + 1, es, i, newSize - i);
        es[size = newSize] = null;
    }

因为arraylist的底层使用的数组存储的元素,所以,当我们做删除动作的时候,实际上我们不光要删除那个位置上的元素,还要把删除位置的后续元素整体往前移动一位。

迭代

java 复制代码
        ArrayList<Integer> list = new ArrayList<>();
        list.add(0);
        list.add(1);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        
        for (Integer integer : list) {
            System.out.println(integer);
        }

我代码里面给出了两种arraylist的迭代方式,一种是简单的for循环遍历,一种是foreach循环。不过,foreach看上去更为简洁,而且它普遍适用于各种容器。这种foreach语法背后是怎么实现的呢?其实,编译器会将它转换为类似如下代码:

java 复制代码
        Iterator<Integer> it = intList.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

要想实现foreach循环,就必须要是实现Iterator接口

java 复制代码
        public interface Iterator<E> {
            boolean hasNext();
            E next();
            void remove();
        }

使用foreach循环中新手可能会遇见 ConcurrentModificationException可以看一下我写的这篇文章。

ArrayList还实现了ListIterator接口,这个接口在原有的Iterator的基础上扩展了一些能力。

java 复制代码
public interface ListIterator<E> extends Iterator<E> {
    boolean hasNext();
    E next();
    boolean hasPrevious();
    E previous();
    int nextIndex();
    int previousIndex();
    void remove();
    void set(E e);
    void add(E e);
}

** 迭代器的好处**

我们介绍了半天迭代器,其实到目前位置,我们还是不太清楚why? 知道怎么用很好,但是更多的是要知道why,我又想起之前高数的时候(虽然学的一般)一位高数老教师说的,不要记住冰冷的定理,要记住背后火热的思考。所以为什么由迭代器呢?

迭代器实际上是一种通用的遍历的方式,它适用于各种容器类;比如fori循环对于ArrayList是很合适,但是对于Set或者Map就不合适。所以我们搞出来了迭代器。迭代器实际上是一种关注点分离的思想,将数据的实际组织方式与数据的迭代遍历隔离开。这样我们只需要拿到迭代器接口的一个引用,不需要再关注数据的实际组织方式是list还是set还是Map了,就可以用一种统一的方式进行访问。

总结

ArrayList,它的特点是内部采用动态数组实现,这决定了以下几点。

  1. 可以随机访问,按照索引位置进行访问效率很高,时间复杂度是O(1)
  2. 如果非排序好的数组,查找的时间复杂度是O(N)
  3. 添加元素的效率还可以,虽然有时候需要重新分配并且要复制数组,不过平均来看添加N个元素的效率为O(N)。
  4. 插入和删除元素的效率比较低,因为需要移动元素,时间复杂度O(N)。
相关推荐
考虑考虑3 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261354 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊5 小时前
Java学习第22天 - 云原生与容器化
java
渣哥6 小时前
原来 Java 里线程安全集合有这么多种
java
间彧7 小时前
Spring Boot集成Spring Security完整指南
java
间彧7 小时前
Spring Secutiy基本原理及工作流程
java
Java水解8 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆10 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学11 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole11 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端