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]
- ArrayList implements the RandomAccess interface, indicating that ArrayList supports random access
- ArrayList implements the Cloneable interface, which indicates that ArrayList can clone
- ArrayList implements the Serializable interface, indicating that ArrayList supports serialization
- Unlike vector, ArrayList is not thread safe and can be used in a single thread. Vector or CopyOnWriteArrayList can be selected in multiple threads
- 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
method | explain |
---|---|
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
method | explain |
---|---|
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 🙏