ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

面试题总结-Java集合类系列(1)-ArrayList

2022-06-28 11:38:52  阅读:201  来源: 互联网

标签:index 面试题 Java int ArrayList elementData 数组 size


前言:为什么要写这篇文章?

  现在是知识大爆炸的时代,任何面试题在网上都能找到一堆的答案,我为什么还需要写?

  因为每个人的知识接受范围和接收程度是不一样的,你在网上找到的答案,简单了不稀罕看,答案难了又看不懂,或者文章语气看不习惯的,很少找到了非常适合自己的,很多知识还是碎片式的。

  所以,建议各位朋友也整理一份属于自己的知识文档。

 

1. ArrayList 概述

  ArrayList 是Java开发者最常用的集合类之一,底层是Object数组组成。

  ArrayList 是继承了AbstractList 类,实现了 List 接口。AbstractList 是一个抽象类,也是实现了 List 接口。而 List 接口继承了 Collection  接口。所以,我们需要向上查看接口类,看 List 和 Collection 就可以了。

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable 

 

1.1  ArrayList的常量

  ArrayList 类中,定义了3个常量,官方解释如下:

    /**
     * Default initial capacity.
* * 默认初始化容量,10 */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances.
* * 所有空数组共享的 空数组 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added.
* * 用于默认大小的空实例的共享空数组实例。 * 我们要与EMPTY_ELEMENTDATA 区分开,当添加第一个元素时,用于扩张数组 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

 

1.2 ArrayList 的属性

    /**
     * ArrayList 中保存的具体数据
     * 如果实例化时指定了容量,数组长度与指定容量相同。
     * 如果实例化时是空ArrayList,则使用elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     */
    transient Object[] elementData; 

    /**
     * ArrayList 包含的元素数据量
     */
    private int size;

 

1.3 构造函数

  ArrayList 提供了3个构造方法

    /**
     * 构造一个具有指定初始容量的空列表。
     * initialCapacity 是初始化的容量
     * 当 initialCapacity > 0 时,this.elementData = new Object[initialCapacity];
     * 当 initialCapacity == 0 时,this.elementData = EMPTY_ELEMENTDATA;
     * 其他情况,抛异常
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+  initialCapacity);
        }
    }

    /**
     * 构造一个初始容量为10的空列表。
     * 初始化时就是空列表,只有在第一次调用add方法时,才把数组容量设为10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 构造一个包含指定的元素的列表集合返回它们的顺序迭代器。
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

 

2. Add方法

  Add方法的主要逻辑如下:

  (1) 先判断当前的数组是否有空间添加此元素,判断参数是最小容量 minCapacity =  size + 1

    (1.1) 先判断当前数组是否为空:为空的情况,比较默认值容量 与 minCapacity 谁大,返回大的值;数组不为空就返回 minCapacity 

    (1.2) minCapacity 如果大于当前数组容量,则需要对数组扩容,扩容逻辑参见 grow 方法

  (2) 如果是指定了index的add方法,需要复制数组的数据,原index位置的数据整体往后移。

  (3) 添加元素到指定位置。

  (4) size变量值加1

    /**
     * Appends the specified element to the end of this list.
     * 将指定的元素追加到列表的末尾。
     */
    public boolean add(E e) {
        // 判断存储能力
        ensureCapacityInternal(size + 1);
        // 在队尾添加元素
        elementData[size++] = e;
        return true;
    }

    /**
     * 添加元素到指定的index位置
     * 
     */
    public void add(int index, E element) {
        // 判断index范围,如果>size 或 <0 会抛异常
        rangeCheckForAdd(index);
        // 判断存储能力
        ensureCapacityInternal(size + 1);
        // 转移数字位置,把index位置的元素空出来
        System.arraycopy(elementData, index, elementData, index + 1,  size - index);
        // 在index位置添加元素
        elementData[index] = element;
        size++;
    }

    /**
     * 确认处理能力的方法
     */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    /**
     * 计算当前数组所需的存储容量
     */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 当数组还是空的时候,判断默认存储空间和 minCapacity的值,返回最大的
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    /**
     * 处理存储能力
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0)
            // grow是数组的扩容方法,有需要的自己看一下
            grow(minCapacity);
    }

 

3. Get方法

  Get方法很简单,首先判断了index参数是否合法,再直接从数值取值。

    /**
     * Returns the element at the specified position in this list.
     */
    public E get(int index) {
        // 验证index参数是否大于等于size,是的话抛异常
        rangeCheck(index);
        // 根据数组原理,直接从index位置取值
        return elementData(index);
    }

 

4. Remove方法

4.1 remove(int index)

  根据坐标删除数据,操作如下:

  (1) 验证参数index是否合法

  (2) 查询出要删除的值,最后返回使用

  (3) 计算复制的数组元素数量

  (4) 复制数组,用index后面的数据,整体向前移动,把原index位置的数据覆盖

  (5) 把最后一个数据,设置为null

    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).*/
    public E remove(int index) {
        // 验证index参数是否合法
        rangeCheck(index);

        modCount++;
        // 取出要删除的值
        E oldValue = elementData(index);
        // 计算要复制的数组元素的数量
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 复制数组,把index + 1位置开始的数据,整体往前复制
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
            // 上面是复制操作,所以size--的位置上还保留着一条重复数据,需要设置为null
            elementData[--size] = null; // clear to let GC do its work
        // 返回删除的数据
        return oldValue;
    }    

 

4.2 remove(Object o)

  根据元素删除,先判断元素是否存在,存在就使用刚查到的index,使用快速删除方法删除元素。

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     */
    public boolean remove(Object o) {
        if (o == null) {
            // 如果元素为空,判断null元素的位置
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    // 删除方法
                    fastRemove(index);
                    return true;
                }
        } else {
            // 元素不为空,使用equals 判断,元素的位置
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    // 删除方法
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * 快速删除方法,原理与remove(int index) 一样
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        elementData[--size] = null;
    }

 

标签:index,面试题,Java,int,ArrayList,elementData,数组,size
来源: https://www.cnblogs.com/huanshilang/p/16363888.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有