ArrayList, LinkedList collection source code

1. ArrayList collection source code


1. Features of ArrayList

  • Data structure: based on array implementation, type: object type;
  • Storage elements: orderly and repeatable;
  • Whether the thread is safe: not safe, high efficiency. (without locking mechanism)
  • Features: fast query, slow addition and deletion;
    • Fast query: Because the bottom layer of ArrayList is implemented based on dynamic arrays, the stored data is continuous in memory, and elements can be obtained through subscripts, that is, the get(index) method, and the time complexity is O(1);
    • Slow addition and deletion: The length of the array cannot be changed, and new arrays need to be frequently created, elements copied, and old arrays destroyed (expansion, shrinkage).

Features of Vector:

  • The bottom layer is implemented based on arrays;
  • Fast query, slow increase;
  • Thread synchronization, safe, low efficiency.

2. Time complexity

  • O(1): The element can be found with only one query, for example: get(index) method, based on subscript query;
  • O(n): Need to search from the beginning to the end, for example: query according to the element value; (such as a linked list)
  • O(log n): binary tree, red-black tree.

3. add method and expansion mechanism

3.1 Add method implementation

package com.baidou.list;

public class MyArrayList<T> {
    
    
    /**
     * 存放元素的数组
     */
    Object[] elementData;

    /**
     * elementData 存放元素的个数
     */
    private int size;

    /**
     * elementData 默认初始容量为10
     */
    private static final int DEFAULT_CAPACITY = 10;


    /**
     * 无参构造器
     */
    public MyArrayList() {
    
    
    }


    /**
     * 新增元素
     *
     * @param t 元素
     */
    public void add(T t) {
    
    
        // 默认的初始化
        if (elementData == null) {
    
    
            // 如果创建ArrayList集合时没有指定大小,默认初始长度为10
            elementData = new Object[DEFAULT_CAPACITY]; // CAPACITY:10
        }
        elementData[size++] = t;
    }

    /**
     * 获取元素
     *
     * @param index
     * @return
     */
    public T get(int index) {
    
    
        return (T) elementData[index];
    }

    public static void main(String[] args) {
    
    
        MyArrayList<String> arr = new MyArrayList<String>() {
    
    
            {
    
    
                add("Hello");
                add("world");
            }
        };
        String arr_Index0_Value = arr.get(0);
        System.out.println(arr_Index0_Value);

    }
}

3.2 Expansion mechanism

Expansion: Each expansion of ArrayList is 1.5 times of the original.

1. Source code analysis:

  • Call chain: add()—>ensureCapacityInternal()—>calculateCapacity()—>ensureExplicitCapacity(?)—> grow()
public void add(int index, E element) {
    
    
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

// 模拟ArrayList存放第11个元素场景
private void grow(int minCapacity) {
    
     //11
   
    // 原来容量 10
    int oldCapacity = elementData.length; 
    
    // 新的容量 10+(10/2)=15
    int newCapacity = oldCapacity + (oldCapacity >> 1); 
    
    if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //false   
    
    // 新的容器是否超过这个数值界限
    if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //fase       
   
    // 扩容
    // 拷贝数组,数组长度为15
    elementData = Arrays.copyOf(elementData, newCapacity);
}

2. The add method, the realization of the expansion principle:

public void add(T t) {
    
    
    // 默认的初始化
    if (elementData == null) {
    
    
        // 如果创建ArrayList集合时没有指定大小,默认长度为10
        elementData = new Object[DEFAULT_CAPACITY]; // CAPACITY:10
    }

    // 判断是否需要做扩容
    if (size + 1 > elementData.length) {
    
    
        // 原来容量
        int oldCapacity = elementData.length;
        // 新的容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 扩容
        elementData = Arrays.copyOf(elementData,newCapacity);
    }

    elementData[size++] = t;
}

4. remove method and shrink mechanism

1. Source code analysis:

//根据元素内容删,时间复杂度为O(n)
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++; //因为arraylist是非线程安全,需要记录增删操作
    
    // 模拟集合容量为15,元素个数为11,删除第十个元素
    
    // 计算删除index后面的元素有多少个
    //11-9-1=1;
    int numMoved = size - index - 1;
    if (numMoved > 0) 
 
     	// index后面的元素要向前移动1位
        System.arraycopy(elementData, index+1, elementData, index,numMoved);
                         
    elementData[--size] = null; // 把最后一个元素变为null
}

2. The remove method realizes the shrinking principle:

// 根据内容删除元素
public boolean remove(T t) {
    
    
    if (t == null) {
    
    
        // 如果删除元素内容为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 (t.equals(elementData[index])) {
    
    
                fastRemove(index);
                return true;
            }
        }
    }

    return false;
}

/**
* 缩容实现
*
* @param index 删除元素的索引
*/
private void fastRemove(int index) {
    
    
    // 计算删除index后面的元素有多少个
    int numMoved = size - index - 1;
    if (numMoved > 0) {
    
    
        // 把index后面的元素往前移动1位
        System.arraycopy(elementData, index + 1, elementData, index, numMoved);
    }
    elementData[--size] = null; // 把最后一个元素变为null
}

5. The role of modCount

modCount: Record the number of times the current collection has been modified. Add and remove modCount++

For example: In the case of multi-threading, if thread A traverses the collection and thread B stores data, a concurrent modification exception will be thrown.

@Override
public void forEach(Consumer<? super E> action) {
    
    
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    @SuppressWarnings("unchecked")
    final E[] elementData = (E[]) this.elementData;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
    
    
        action.accept(elementData[i]);
    }
    // 如果当前的modCount和预期count值不一致时,会抛并发修改异常
    if (modCount != expectedModCount) {
    
    
        throw new ConcurrentModificationException();
    }
}    

insert image description here


Two, LinkedList collection source code

Guess you like

Origin blog.csdn.net/qq_46921028/article/details/129905590