【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)。
相关推荐
总是学不会.2 分钟前
第四篇:HTTP 的铠甲——HTTPS 的故事
java·网络协议·http·https·开发
CodeMartain13 分钟前
Arrys.asList踩坑实录
java·开发语言·windows
娶个名字趴14 分钟前
JVM的原理
java·jvm
ThetaarSofVenice15 分钟前
【Java从入门到放弃 之 LinkedList 和 ArrayDeque】
java·开发语言
攻城有术16 分钟前
idea通过本地自己的Tomcat启动,Tomcat日志是乱码的解决方案
java·tomcat·intellij-idea
岁岁岁平安29 分钟前
Maven学习(依赖版本维护、依赖传递、解决Maven依赖冲突的3种方式)
xml·java·学习·maven·pom·依赖冲突问题·依赖传递
IT机器猫33 分钟前
Maven完整技术汇总
java·maven·ssm
广东数字化转型38 分钟前
Less和SCSS,哪个更好用?
开发语言·后端·rust
南宫生38 分钟前
力扣-图论-12【算法学习day.62】
java·学习·算法·leetcode·图论
duration~42 分钟前
Docker Swarm实战
java·docker·容器