Master these, you can achieve hand ArrayList, Vector, and Stack

Today we are going to learn, this data structure is an array of applications in JDK collection.

The simplest array as a linear structure, the operation is relatively simple. Although simple, but it is the programming language underlying implementation indispensable.

It is characterized, according to the index to find quick random, insert and delete slower. Because, according to the index can be positioned directly to an element, and insertion or deletion usually involves migrating data.

Between the front and rear elements of the array is stored contiguously, thus facilitating the CPU cache, thereby increasing the access speed.

Set in the JDK, ArrayList, Vector, and Stack of underlying storage structures, both arrays.

Next, we start from the basic operation of the array, step by step to achieve our goals.

The basic operation of the array

There are an array of three basic operations: search, insert, and delete.

Definition array

/** 创建一个数组 */
int[] elementData = new int[10];

/** 数组已存储的元素个数 */
int size = 0;
复制代码

Simple operation

Add to

/**
 * 添加. 将新元素添加到数组尾部.
 */
public void add(int e) {
    elementData[size++] = e;
}
复制代码

Seek

/**
 * 查找. 获取指定索引位置的元素
 */
public int get(int index) {
    return elementData[index];
}
复制代码

Complex operation

insert

/**
 * 插入.
 * 1.指定索引位置及之后的所有元素先往后移动1位; 
 * 2.将新元素插入到指定索引位置.
 */
public void add(int index, int e) {
    // 1.index及之后的元素后移1位. index移动到index+1的位置,移动的数量为size-index个.
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    
    // 2.保存元素,已存储数量加1
    elementData[size++] = e;
}
复制代码

delete

/**
 * 删除. 删除指定索引位置的元素,指定索引位置后面的所有元素往前移动1位.
 */
public void remove(int index) {
    // 1.计算出需要往前移动的元素个数.
	// index+1代表区间[0,index]的数量,size-(index+1)代表index之后元素的个数.
    int numMoved = size - index - 1;
    
    // 2.将index后面的所有元素往前移动1位.
    // 伪代码:for (numMoved) elementData[index]=elementData[index+1];
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    
    // 3.已存储数量减1
    size--;
}
复制代码

Extended operation

Update

/**
 * 更新. 更新指定索引位置的元素,并返回旧值.
 */
public int set(int index, int newValue) {
    int oldValue = elementData[index];
    elementData[index] = newValue;
    return oldValue;
}
复制代码

Finding Elements

/**
 * 查找元素. 返回首次出现的索引位置,如果元素不存在则返回 -1.
 */
public int indexOf(int o) {
    for (int i = 0; i < size; i++)
        if (o == elementData[i])
            return i;
    return -1;
}
复制代码

Expansion

/**
 * 默认进行2倍扩容.
 */
private void resize() {
	if (size == elementData.length) {
		// 1.按2倍大小创建新数组,并将已存储的数据迁移到新数组
		int[] copy = new int[size * 2];
		System.arraycopy(elementData, 0, copy, 0, elementData.length);
		
		// 2.使用扩容后的新数组替换旧数组
		elementData = copy;
	}
}
复制代码

Expand knowledge

Achieve System.arraycopy

/**
 * System.arraycopy的简化实现.
 */
public void arraycopy(int[] src, int srcPos, int[] dest, int destPos, int length) {
	for (int i = 0; i < length; i++) {
		dest[destPos++] = src[srcPos++];
	}
}
复制代码

summary

From the foregoing, we can understand that the array is slightly more complicated operation to insert, delete and expansion .

In common as follows:

1. Where to start moving, which direction to move?

Insert an element

  • And after the specified index (> = index) of all the elements of a first backward movement;
  • Then insert a new element to the specified index.

To delete an element

  • All later specified index (> index) of the forward movement of an element . In this way, the element is covered with the specified index, corresponding to the deleted.

2. how many elements need to move?

Inserting the number of elements to be moved to: size - index

Remove the number of elements to be moved to: size - (+ index. 1)

3. Mobile Data

The number of index positions and the moving mobile, mobile data cycle starts in accordance with the foregoing two points obtained.

Although their mobile data can be achieved, but by means of System.arraycopy () is more efficient . Because the high-performance JVM will System.arraycopy () method is implemented as intrinsic, i.e., inside the JVM will provide a more efficient implementation.

Achieve ArrayList

These combinations of the above operations up the array, can be achieved in the core function ArrayList.

The main idea is as follows:

  • If the index related to the reference index, for the first index range validity check (rangeCheck);
  • When data is inserted or added, to carry out capacity check and expansion (a resize);
  • Will be used instead of the generic type int E, to support the generic type value ;
  • When deleting the last element is set to null, in order to facilitate the GC (elementData of [- size] = null) ;
  • indexOf comparison elements used equals instead of equal numbers ;
  • Contains the specified elements: boolean contains (Object o) {return indexOf (o)> = 0;};
  • Returns list size: public int size () {return size;};
  • If the list is empty: boolean isEmpty () {return size == 0;};

Realization Vector

Basic and ArrayList is the same to achieve the Vector , the main difference is that Vector is thread-safe .

Vector thread-safe method to achieve is very simple, that is, on the basis of the ArrayList, equal treatment for each method plus synchronization primitives the synchronized .

Simple and crude result is that in the case of high concurrent relatively low efficiency, it is generally used is relatively small.

Stack realization

Stack inherited Vector , simply reuse the relevant methods of Vector. Based on these methods, the stack package stack pop operation.

One can imagine, Stack related operations is thread-safe, efficiency depends on the implementation of the Vector.

Array index 0 represents the position of bottom of the stack, the index representative of the position of the stack size-1.

Get the top element peek ()

Find the equivalent index number for the size-1 elements: GET (size - 1) .

Stack push ()

The push element stack, i.e. elements to the end of the array: the Add (E) .

Pop pop ()

The top element from the stack, namely to delete the last element of the array: the Remove (size - 1) .

to sum up

I write to you, we summarize key step in the grasp In this part:

  1. Learn the basic operation of the array, the focus is to insert, delete and expansion;
  2. Based on the basic operation of the array, improve and implement ArrayList;
  3. On the basis of the ArrayList on, plus synchronization primitives synchronized for all methods to achieve Vector;
  4. Several basic methods inherited Vector, utilized to achieve a stack Stack stack operation.

Through the above steps, learning to be more efficient, better understanding ArrayList, Vector, and Stack class that implements these principles.

Well, now it's just the last step: turn on the computer, start unarmed realize it ^ _ ^

Finally, attach my ArrayList simplify implementation:

/**
 * java.util.ArrayList的核心实现.
 *
 * @param <E>
 */
public class MyArrayList<E> {

	/** 用于存储元素的数组. 其大小,即为底层存储的容量. */
    private Object[] elementData;
    /** 已存储元素的个数 */
    private int size;
	
    /**
     * 默认构造函数
     */
    public MyArrayList() {
    	this(10);
    }
    
    /**
     * 构造函数
     * 
     * @param initialCapacity 初始化容量
     */
    public MyArrayList(int initialCapacity) {
        this.elementData = new Object[initialCapacity];
    }
    
    /**
     * 添加元素
     * 
     * @param e
     * @return
     */
    public boolean add(E e) {
    	// 1.检查容量与扩容
        resize();
        // 2.保存元素,已存储数量加1
        elementData[size++] = e;
        return true;
    }
    
    /**
     * 插入. 将元素插入到指定索引位置,索引位置及之后的元素往后移动1位
     * 
     * @param index
     * @param e
     */
    public void add(int index, E e) {
    	// 1.索引范围检查
    	rangeCheckForAdd(index);
    	// 2.检查容量与扩容
        resize();
        // 3.index及之后的元素后移1位
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        // 4.保存元素,已存储数量加1
        elementData[size++] = e;
    }
    
    /**
     * 获取指定索引位置的元素
     * 
     * @param index
     * @return
     */
    @SuppressWarnings("unchecked")
	public E get(int index) {
    	// 1.索引范围检查
        rangeCheck(index);
        // 2.返回元素
        return (E) elementData[index];
    }
    
    /**
     * 更新指定索引位置的元素,同时返回旧值.
     * 
     * @param index
     * @param element
     * @return
     */
    @SuppressWarnings("unchecked")
	public E set(int index, E element) {
    	// 1.索引范围检查
        rangeCheck(index);
        // 2.暂存旧值,在最后返回
        E oldValue = (E) elementData[index];
        // 3.更新指定索引位置的数量
        elementData[index] = element;
        
        return oldValue;
    }
    
    /**
     * 删除指定索引位置的元素,并返回该元素
     * 
     * @param index
     * @return
     */
    @SuppressWarnings("unchecked")
	public E remove(int index) {
    	// 1.索引范围检查
        rangeCheck(index);
        // 2.获取待删除后返回的元素
        E oldValue = (E) elementData[index];
        // 3.将index后面的所有元素往前移动1位
        // 计算出需要往前移动的元素个数. index+1代表区间0~index的数量,size-(index+1)代表index之后元素的个数.
        int numMoved = size - index - 1;
        if (numMoved > 0)
        	// 将[index+1, size)区间的numMoved个元素往前移动1位
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        // 4.已存储数量减1. 同时末尾元素设置为null,便于GC.
        elementData[--size] = null;

        return oldValue;
    }
    
    /**
     * 是否包含指定元素
     * 
     * @param o
     * @return
     */
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    /**
     * 返回首次出现指定元素的索引位置. 如果元素不存在则返回 -1.
     * 
     * @param o
     * @return
     */
    public int indexOf(Object o) {
    	// 从前往后遍历
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
        return -1;
    }
    
    /**
     * 返回最后一次出现指定元素的索引位置. 如果元素不存在则返回 -1.
     * 
     * @param o
     * @return
     */
    public int lastIndexOf(Object o) {
    	// 从后往前遍历
    	for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
        return -1;
    }
    
    /**
     * 以数组形式返回列表的所有元素,支持泛型
     * 
     * @param a
     * @return
     */
    public <T> T[] toArray(T[] a) {
        System.arraycopy(elementData, 0, a, 0, size);
        return a;
    }
    
    /**
     * 返回列表大小
     * 
     * @return
     */
    public int size() {
        return size;
    }

    /**
     * 列表是否为空
     * 
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }
    
    /**
     * 索引范围检查
     * 
     * @param index
     */
    private void rangeCheck(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("size: " + size + ", index: " + index);
    }

    /**
     * 索引范围检查
     * 
     * @param index
     */
    private void rangeCheckForAdd(int index) {
    	if (index < 0 || index > size)
    		throw new IndexOutOfBoundsException("size: " + size + ", index: " + index);
    }

    /**
     * 检查数组存储空间是否已满,如果满了进行1.5倍扩容.
     */
	private void resize() {
		if (size == elementData.length) {
			// 1.默认按1.5倍扩容
			int newCapacity = size + (size >> 1);
			if (newCapacity <= size) {
				newCapacity = size + 1;
			}
			// 2.扩容,将元素迁移到新的数组
			elementData = copyOf(elementData, newCapacity);
		}
	}
	
	/**
	 * 从源数组复制指定长度的元素到新数组. 用于扩容或缩容
	 * 
	 * @param original
	 * @param newLength
	 * @return
	 */
	private Object[] copyOf(Object[] original, int newLength) {
		Object[] copy = new Object[newLength];
		System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
		return copy;
	}
    
}
复制代码

Figure question: thejavaprogrammer.com

Personal Public Number

For more articles, please pay attention to the public number: Binary road

Binary road

Guess you like

Origin juejin.im/post/5e3e94a4518825494c75d70c