ArrayList

作者: 有腹肌的豌豆Z | 来源:发表于2020-09-11 16:13 被阅读0次

名词解释

  • ArrayList是集合的一种实现,实现了接口List,List接口继承了Collection接口。Collection是所有集合类的父类
  • ArrayList 是 java 集合框架中比较常用的数据结构了。继承自 AbstractList,实现了 List 接口。
  • 底层基于数组实现容量大小动态变化。
  • 允许 null 的存在。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。

成员变量

/**
* The size of the ArrayList (the number of elements it contains).
* 注意: size 是指 elementData 中实际有多少个元素,而 elementData.length 为集合容量,表示最多可以容纳多少个元素。
*/
private int size;  
transient Object[] elementData; 
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];

/**
* Default initial capacity.默认初始容量大小为 10;
*/
private static final int DEFAULT_CAPACITY = 10;

构造函数

    /**
     * Constructs an empty list with an initial capacity of ten.
     * 这里创建了一个默认容量为10的list
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 创建一个自定义大小的list 
     */
    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);
        }
    }

    /**
     * 可以传递任何实现了Collection接口的类
     */
     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;
        }
    }

定义一个ArrayList

//默认创建一个ArrayList集合
List<String> list = new ArrayList<>();
//创建一个初始化长度为100的ArrayList集合
List<String> initlist = new ArrayList<>(100);
//将其他类型的集合转为ArrayList
List<String> setList = new ArrayList<>(new HashSet());
  • 当调用new ArrayList<>()时,将一个空数组{}赋值给了elementData,这个时候集合的长度size为默认长度0。
  • 当调用new ArrayList<>(100)时,根据传入的长度,new一个Object[100]赋值给elementData,当然如果玩儿的话,传了一个0,那么将一个空数组{}赋值给了elementData。
  • 当调用new ArrayList<>(new HashSet())时,根据源码,我们可知,可以传递任何实现了Collection接口的类,将传递的集合调用toArray()方法转为数组内赋值给elementData。

ArrayList常用方法

1. add(E element)

  • 我们通过源码来看一下add("白骨精")到底发生了什么
   public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
  • 首先通过 ensureCapacityInternal(size + 1) 来保证底层Object[]数组有足够的空间存放添加的数据,然后将添加的数据存放到数组对应的位置上,我们看一下是怎么保证数组有足够的空间?
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int var1) {
        ++this.modCount;
        if (var1 - this.elementData.length > 0) {
            this.grow(var1);
        }
    }
  • 这里首先确定了Object[]足够存放添加数据的最小容量,然后通过 grow(int minCapacity) 来进行数组扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

2. set(int index, E element)

  • 因为ArrayList底层是由数组实现的,set实现非常简单,调用 set(8, "猪八戒") 通过传入的数字下标找到对应的位置,替换其中的元素,前提也需要首先判断传入的数组下标是否越界。将“猕猴王”替换为“猪八戒”。
public E set(int index, E element) {
    rangeCheck(index);
    E oldValue = elementData(index);
    elementData[index] = element;
    //返回值“猕猴王”,当前数组中数据:
    return oldValue;
}

3. get(int index)

  • ArrayList中get方法也非常简单,通过下标查找即可,同时需要进行了类型转换,因为数组为Object[],前提是需要判断传入的数组下标是否越界。
public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}
E elementData(int index) {
    return (E) elementData[index];
}

4. remove(int index)

  • 首先说一下ArrayList通过下标删除的方法,我们看一下源码
public E remove(int index) {
    rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}
  • 通过源码我们可以看到首先获取了待删除的元素,并最终返回了。其次计算了数组中需要移动的位数 size - index - 1,那么很明显我们可以得出待删除的是最后一个元素的话,移到位数为0,否则移动位数大于0,那么通过数组元素的拷贝来实现往前移动相应位数。

5. remove(Object o)

  • 删除ArrayList中的值对象,其实和通过下标删除很相似,只是多了一个步骤,遍历底层数组elementData,通过equals()方法或 == (特殊情况下)来找到要删除的元素,获取其下标,调用remove(int index)一样的代码即可。
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

 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; // clear to let GC do its work
    }

6.size() : 获取集合长度,通过定义在ArrayList中的私有变量size得到

 public int size() {
        return size; // 成员变量 
 }

7.isEmpty():是否为空,通过定义在ArrayList中的私有变量size得到

public boolean isEmpty() {
        return size == 0;
}

8.contains(Object o):是否包含某个元素,通过遍历底层数组elementData,通过equals或==进行判断

public boolean contains(Object o) {
        return indexOf(o) >= 0;
}

public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
 }

9.clear():集合清空,通过遍历底层数组elementData,设置为null

public void clear() {
        modCount++;
        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
 }

相关文章

网友评论

      本文标题:ArrayList

      本文链接:https://www.haomeiwen.com/subject/ljuijktx.html