java data structure - 2 array-based ArrayList

2. Array-based ArrayList

ArrayList is the implementation class of the most commonly used List interface in Java, which is implemented internally by maintaining an unordered array. So ArrayList has all the advantages and disadvantages of not having arrays:

  • Operation ———————— Time Complexity
  • Insert —————— O(1)
  • Delete —————— O(n)
  • Find------------ O(n)

have to be aware of is:

1. ArrayList always adds the element to the first non-empty position in the array: When we add an element to the ArrayList, in order to ensure that the element is inserted in O(1) time, the ArrayList always adds the element to the first non-empty position in the array. A non-empty position is achieved by maintaining the size variable, which represents the number of elements that have been added in the array. When we insert a piece of data, we can directly add this element to the position of size+1 in the array.

2. There are no empty elements in the array maintained in ArrayList. This means that when you delete an element in an array, all subsequent elements in the array are moved forward by one position. When we delete an element, size becomes size-1, and if this element is not the last element in the array, it means that although there are only size-1 elements, there is a position element between 0 and size that is empty, and There are elements at the size position. When inserting an element next time, add +1 on the basis of size-1, that is, inserting an element at the size position, the element at the original size position will be overwritten.

3. The arrays maintained in ArrayList need to be dynamically expanded. Since the size of the array is fixed once it is created. Therefore, when the size of the array maintained in the ArrayList reaches the limit, the array must be copied to a larger array.

ArrayList source code analysis:

Two important variables are maintained in the source code of ArrayList:

transient Object[] elementData; // 用于存放元素的数据,数组的大小就是ArrayList的容量capacity
private int size;//数组中已经存放的元素的数量

1) Add elemental analysis

Adding an element is done with the add method:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 确保elementData数组中还有空间插入新的元素
    elementData[size++] = e;//在数组的最后一个插入元素
    return true;
}

The ensureCapacityInternal method ensures that there is still space to insert new elements in the elementData array, that is, the current elementData.length>size. If elementData.length==size, it means that the array needs to be dynamically expanded.

Expansion is achieved by calling the grow method:

private void grow(int minCapacity) {//minCapacity表示的是需要扩容最小容量
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);//默认扩容为1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)//这个用于保证数组的容量最大不会超过2的30次方-1
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //使用计算出需要扩大到的新的容量创建一个新数组,并将elementData[]的数组中元素拷贝到新的数组中,再重新赋值给elementData[]。
    elementData = Arrays.copyOf(elementData, newCapacity);
}

minCapacity indicates the minimum capacity that needs to be expanded. For example, assuming that the length of the current elementData[] array is capacity, then when adding an element, in theory, it only needs to expand the capacity of the array to capacity+1, then minCapacity=capacity+1. However, since the contents of the old array need to be copied after creating a new array, the copy operation is very resource-intensive. If the capacity after the expansion is only +1 on the current basis, then the next time you add an element, you will need to expand the capacity and copy the array again. In order to avoid this situation, there will be a default expansion ratio during expansion, which is the newCapacity in the code. It can be seen from the code that the expansion is 1.5 times. If newCapacity>minCapacity needs to be expanded, newCapacity will be used as the capacity of the new array.

2) Analysis of delete operation remove method

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; //删除size位置上的元素,同时将size-1,再将这个位置上的元素置为null以便垃圾回收

    return oldValue;//返回删除的元素的值。
}

3) Find Operation Analysis

If you want to get the element at the specified position, you can call the get(int index) method. The time complexity of this method is O(1). But all the searches we do here mean that we don't know where an element is, so we can only use thread search. Therefore, when we need to traverse the ArrayList, suppose there are N elements. When we traverse, according to experience, we need to traverse N/2 times on average, so the time complexity is O(N).

The indexOf and lastIndexOf methods of ArrayList both find the position of an object in the ArrayList by traversing the method.

public int indexOf(Object o) {
    if (o == null) {//如果对象是null,返回elementData[]中第一个元素值为null的下标
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {////如果对象不是null,返回elementData[]中第一个equals方法相等的index
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;//查找不到返回-1
}

Similarly, lastIndexof(Object obj) is searched from back to front. The reason for these two search methods is that duplicate elements can be added to the ArrayList.

Here is the code implementation:

public class SimpleArrayList<V> {
    private Integer element_size = 0;//数组中元素的个数
    private Integer array_capacity = 16;//创建SimpleArrayList时,数组的容量,默认为16
    private final static Integer DEFUALT_EXPAND_SIZE = 16;//当数组容量不足时,默认每次扩容的大小
    private Object[] array = null;

    public SimpleArrayList() {
        this(DEFUALT_EXPAND_SIZE);
    }
    /**
     * 数组大小
     */
    public SimpleArrayList(Integer array_capacity) {
        super();
        if (array_capacity <= 0) {
            throw new IllegalArgumentException("集合容量必须大于0");
        }
        array = new Object[array_capacity];
        this.array_capacity = array_capacity;
    }
    /**
     * 插入一个新的元素,如果数组可以放下,直接添加;如果数组放不下,扩容
     */
    public void add(V v){
        if (element_size < array_capacity) {
            array[element_size++] = v;
        }else {
            array_capacity += DEFUALT_EXPAND_SIZE;
            Object[] new_array = new Object[array_capacity];
            System.arraycopy(array, 0, new_array, 0, array.length);
            array = new_array;
            array[element_size++] = v;
        }
    }
    /**
     * 根据指定下标查找元素
     */
    public V get(int index){
        if (index < 0 || index > element_size - 1) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        return (V)array[index];
    }
    /**
     * 删除指定位置的元素,所有之后的元素前移
     */
    public void remove(int index){
        if (index < 0 || index > element_size - 1) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        for (int i = index; i < element_size - 1; i++){
            array[i] = array[i + 1];
        }
        element_size--;
    }
    /**
     * 更新指定位置的元素
     */
    public void update(int index, V v){
        if (index < 0 || index > element_size - 1) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        array[index] = v;
    }
    /**
     * 返回集合大小,集合容量
     */
    public Integer size(){
        return element_size;
    }
    public Integer capacity(){
        return array_capacity;
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325490179&siteId=291194637