[JavaSE] ArrayList and sequence table

Posted by mtombs on Tue, 04 Jan 2022 01:45:19 +0100

1 Introduction to ArrayList

In the collection framework, ArrayList is a common class that implements the List interface. The specific framework diagram is as follows:

[description]

  1. ArrayList implements the RandomAccess interface, indicating that ArrayList supports random access
  2. ArrayList implements the Cloneable interface, which indicates that ArrayList can clone
  3. ArrayList implements the Serializable interface, indicating that ArrayList supports serialization
  4. Unlike vector, ArrayList is not thread safe and can be used in a single thread. Vector or CopyOnWriteArrayList can be selected in multiple threads
  5. The bottom layer of ArrayList is a continuous space and can be dynamically expanded. It is a dynamic type sequential table

2. Use of ArrayList

2.1 structure of ArrayList

methodexplain
ArrayList()Nonparametric structure
ArrayList(Collection<? extends E> c)Building ArrayList from other collections
ArrayList(int initialCapacity)Specifies the initial capacity of the sequence table

💡 Several examples:

public class Test {
    public static void main(String[] args) {
        // It is recommended to create ArrayList
        // Construct a list of ten elements
        List<Integer> list1 = new ArrayList<>(10);
        //Note: List has been specified as a List of Integer type, so only Integer type data can be stored
        list1.add(21);
        list1.add(12);
        list1.add(43);
        List<Integer> list2 = new ArrayList<>(list1);
        int ret = list2.get(2);
        System.out.println(ret);
    }
}

2.2 common methods of ArrayList

methodexplain
boolean add(E e)Tail insert e
void add(int index, E element)Insert e into the index position
boolean addAll(Collection<? extends E> c)Trailing elements in c
E remove(int index)Delete index location element
boolean remove(Object o)Delete the first o encountered
E get(int index)Get subscript index position element
E set(int index, E element)Set the subscript index position element to element
void clear()empty
boolean contains(Object o)Judge whether o is in the linear table
int indexOf(Object o)Returns the subscript of the first o
int lastIndexOf(Object o)Returns the subscript of the last o
List subList(int fromIndex, int toIndex)Intercepted part list

🔥 Usage example:

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("JavaSE");
        list.add("JavaWeb");
        list.add("JavaEE");
        list.add("JVM");
        list.add("Test course");
        System.out.println(list);
        // Get the number of valid elements in the list
        System.out.println(list.size());
        // Get and set the element at the index location. Note that the index must be between [0, size]
        System.out.println(list.get(1));
        list.set(1, "JavaWEB");
        System.out.println(list.get(1));
        // Insert the specified element at the index position of the list, and the index and subsequent elements will move back one position
        list.add(1, "Java data structure");
        System.out.println(list);
        // Delete the specified element. If it is found, it will be deleted. The elements after the element will move forward one position
        list.remove("JVM"); System.out.println(list);
        // Delete the element at the index position in the list. Note that the index should not exceed the number of valid elements in the list, otherwise the subscript out of bounds exception will be thrown
        list.remove(list.size()-1); 
        System.out.println(list);
        // Check whether the list contains the specified element, and return true; otherwise, return false
        if(list.contains("Test course")){
            list.add("Test course");
        }
        // Find the location where the specified element first appears: indexOf from front to back, lastIndexOf from back to front
        list.add("JavaSE");
        System.out.println(list.indexOf("JavaSE"));
        System.out.println(list.lastIndexOf("JavaSE"));
        // Use the elements between [0, 4) in the list to form a new ArrayList return
        List<String> ret = list.subList(0, 4);
        System.out.println(ret);
        list.clear(); 
        System.out.println(list.size());
    }

2.3 traversal of ArrayList

ArrayList can be traversed in three ways: for loop + subscript, foreach and iterator
🔑 Code demonstration:

List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        // Traversal with subscript + for 
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
        // Traversal with foreach
        for (Integer integer : list) {
            System.out.print(integer + " ");
        }
        System.out.println();
        Iterator<Integer> it = list.listIterator();
        while(it.hasNext()){
            System.out.print(it.next() + " ");
        }
        System.out.println();

2.4 capacity expansion mechanism of ArrayList

Let's start with a code:

public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        System.out.println(list);
    }

Since we did not specify the size of the list, we generally think that this code will report an error. However, due to the automatic capacity expansion mechanism of ArrayList, when the list space is not enough to store elements, the list will be expanded first and then put the elements into the list. So this code can run normally.


Let's take a look at the capacity expansion in the ArrayList source code:

    transient Object[] elementData;// Space to store elements
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};// Default space
    private static final int DEFAULT_CAPACITY = 10;// Default capacity size
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // Get old space size
        int oldCapacity = elementData.length;
        // 1.5x capacity expansion
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // Determine new space size
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // Capacity expansion
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0){
            //Error in space size, error thrown.
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
    }

Summary:
First, judge whether the list is out of bounds after adding an element (i.e. size+1). If it is out of bounds, expand the capacity. Otherwise, directly assign the element value. Note that when the initial space is 0, the capacity will be automatically expanded to 10, which will reduce the number of capacity expansion and optimize the efficiency

4. Playing cards with ArrayList

💡 Code demonstration:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * Created with IntelliJ IDEA. 
 * Description:
 * User: admin
 * Date: 2021-11-29
 * Time: 22:08
 */
class Card{
    private int rank;
    private String suit;

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public String getSuit() {
        return suit;
    }

    public void setSuit(String suit) {
        this.suit = suit;
    }

    @Override
    public String toString() {
        return "["+this.suit+":"+this.rank+"]";
    }
}
public class Test {
    public static final String[] suits = {"♠","♦","♣","♥"};
    //Buying cards generates a list of stored cards
    public static List<Card> buyCards(){
        List<Card> cards = new ArrayList<>();
        for (int i = 0; i < suits.length; i++) {
            for (int j = 1; j <= 13; j++) {
                Card card = new Card(j,suits[i]);
                cards.add(card);
            }
        }
        return cards;
    }
    //shuffle the cards
    public static List<Card> swapCards(List<Card> cards){
        for (int i = cards.size()-1; i>0; i--){
            Random random = new Random();
            //The range of generated random number is [0, i). Each cycle allows the last element to exchange with the previous random element, so as to achieve the effect of shuffling
            int rand = random.nextInt(i);
            Card tep = cards.get(rand);
            cards.set(rand,cards.get(i));
            cards.set(i,tep);
        }
        return cards;
    }
    public static void main(String[] args) {
        List<Card> cards = buyCards();
        List<Card> ret = swapCards(cards);
        System.out.println(ret);
        //Create a hand list. Each element is a list < card >, representing the cards in someone's hand
        List<List<Card>> hand = new ArrayList<>();
        List<Card> hand1 = new ArrayList<>();
        List<Card> hand2 = new ArrayList<>();
        List<Card> hand3 = new ArrayList<>();
        hand.add(hand1);
        hand.add(hand2);
        hand.add(hand3);
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 3; j++) {
                Card card = cards.remove(0);
                hand.get(j).add(card);
            }

        }
        System.out.println("The first card is:"+hand.get(0));
        System.out.println("The second card is:"+hand.get(1));
        System.out.println("The third card is:"+hand.get(2));
        System.out.println("Remaining cards:"+cards);
    }
}

5 Simulation Implementation of ArrayList

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2021-11-30
 * Time: 23:09
 */
//Avoid warnings when casting Object types
@SuppressWarnings("all")
class MyArrayList<E> {
    private Object[] array;//array
    private int size;//Number of elements actually stored in the array
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    public MyArrayList() {
        this.array = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    public MyArrayList(int len) {
        if (len > 0) {
            this.array = new Object[len];
        } else if (len == 0) {
            this.array = new Object[0];
        } else {
            throw new IllegalArgumentException("Subscript error");
        }
    }
    public int size(){
        return this.size;
    }
    public boolean isEmpty(){
        return 0==this.size;
    }
    public boolean add(E e){
        //Ensure that the array can be placed after adding elements. If it cannot be placed, it will be automatically expanded
        ensureCapacityInternal(size + 1);
        this.array[this.size] = e;
        this.size++;
        return true;
    }
    public void ensureCapacityInternal(int minCapacity){
        //1. Calculate first
        int capacity = calculateCapacity(array, minCapacity);
        //2. Ensure that the capacity can be allocated
        ensureExplicitCapacity(capacity);
    }
    private static final int DEFAULT_CAPACITY = 10;
    public int calculateCapacity(Object[] array, int minCapacity){
        if(array == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            return Math.max(DEFAULT_CAPACITY,minCapacity);
        }
        return minCapacity;
    }
    public void ensureExplicitCapacity(int capacity){
        if(capacity-this.array.length>0){
            grow(capacity);
        }
    }
    //Capacity expansion
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    public void grow(int capacity){
        int oldCapacity = this.array.length;
        int newCapacity = oldCapacity + (oldCapacity>>1);
        if(newCapacity<capacity){
            newCapacity = capacity;
        }
        if(newCapacity>MAX_ARRAY_SIZE){
            newCapacity = MAX_ARRAY_SIZE;
        }
        this.array = new Object[newCapacity];
    }
    //When adding an element to a subscript, note that adding or deleting a subscript element must be limited to judge the validity of the subscript.
    public void add(E e, int index){
        check(index);
        ensureCapacityInternal(this.size+1);
        for (int i = size-1; i >= index ; i--) {
            array[i+1] = array[i];
        }
        //If e is an Object type, there is no type checking function, which may cause the array to be placed in different types of elements.
        array[index] = e;
        size++;

    }
    //Check subscript validity
    public void check(int index){
        if(index<0 || index>=this.size){
            throw new IndexOutOfBoundsException("Subscript out of bounds");
        }
        return;
    }
    //Delete element
    public void delete(int index){
        check(index);
        for (int i = index+1; i < size; i++) {
            array[i-1] = array[i];
        }
        array[size-1] = null;
        size--;
    }
    // Gets the location where num first appears
    public int getPos(Object num){
        if(num == null){
            for (int i = 0; i < size; i++) {
                if(array[i] == null){
                    return i;
                }
            }
        }else{
            for (int i = 0; i < size; i++) {
                if(array[i].equals(num)){
                    return i;
                }
            }
        }
        System.out.println("There is no subscript, please re-enter!");
        return -1;
    }

    public boolean remove(Object num){
        int index = getPos(num);
        if(index == -1){
            System.out.println("There is no such element!");
            return false;
        }
        delete(index);
        return true;
    }

    public E getNum(int index){
        check(index);
        return (E) array[index];
    }

    public E setNum(int index,E e){
        check(index);
        array[index] = e;
        return e;
    }

    public void clear(){
        for (int i = 0; i < size; i++) {
            array[i] = null;
        }
        size = 0;
    }

    @Override
    public String toString() {
        String str = "[";
        if(size>0){
            //The last number is placed outside the loop without a semicolon.
            for (int i = 0; i < size-1; i++) {
                str += array[i];
                str += ";";
            }
            str += array[size-1];
        }
        str += "]";
        return str;
    }
}
public class Test {
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();
        list.add("hello");
        list.add("world");
        int pos = list.getPos("world");
        System.out.println(list);
        System.out.println(pos);
        list.remove("hello");
        System.out.println(list);
        String RET = list.getNum(0);
        System.out.println(RET);
    }
}

The author's level is limited. If there are any questions in the article, please chat or leave a message. I hope to learn and progress with you!!!
Creation is not easy, I hope you again 👍 Thanks for your support 🙏

Topics: Java Back-end intellij-idea