In-depth source code analysis of ArrayList: exploring the mechanism and performance of Java dynamic arrays

Insert image description here

1. Introduction ArrayList

1.1 Introduce the basic concepts and functions of ArrayList

In Java, ArrayList is a dynamic array that implements the List interface. It automatically grows in size as needed, so any number of elements can be stored.

  1. basic concept:
    • ArrayList is one of the commonly used collection classes in Java, which can store objects, and these objects can be accessed and manipulated based on indexes.
    • ArrayList is implemented based on an array, but it has the ability to dynamically expand, so the number of elements can be increased and decreased dynamically.
  2. effect:
    • Storing data: ArrayList can be used to store various types of data, including basic types and object types.
    • Dynamic expansion: Since the size of ArrayList is dynamic, it is very suitable for scenarios where elements need to be dynamically added and reduced.
    • Convenient operation: ArrayList provides a wealth of methods to operate elements, such as adding, deleting, searching, etc., making the operation of collections very convenient.

In short, ArrayList is a very commonly used data structure in Java. It provides the ability to dynamically store data and rich operation methods, which is very suitable for use in development.

1.2 Differences and advantages from arrays

ArrayList is a dynamic array, which is implemented based on arrays but has the ability to dynamically expand and contract. Compared with ordinary arrays, ArrayList has the following differences and advantages:

  1. Size dynamic: The size of ArrayList is dynamic and can be dynamically expanded and contracted as needed. The size of a normal array is fixed and cannot be changed once created.
  2. Automatic expansion: When the number of elements in the ArrayList exceeds the current capacity, the ArrayList will automatically expand, while ordinary arrays require manual reallocation of memory and copying of data.
  3. Inserting and deleting elements is efficient: ArrayList supports inserting and deleting elements at any position, while ordinary arrays need to move other elements when inserting and deleting elements.
  4. Built-in methods and functions: ArrayList provides many convenient methods and functions, such as adding, deleting, searching and other operations, making it easier to use and operate.

In general, ArrayList is more flexible, convenient, and has higher operating efficiency than ordinary arrays. Therefore, in most cases, using ArrayList is more convenient and practical than using ordinary arrays.

2. Internal implementation

2.1 Data structure: dynamic array

In Java, ArrayList is a class that implements dynamic arrays. It is a dynamic array based on arrays and can automatically expand. The following is the dynamic array principle of ArrayList:

  1. Internal array: ArrayList uses an array internally to store elements. When an ArrayList is created, an array of initial capacity is initialized.
  2. Automatic expansion: When adding elements to ArrayList, if the current array is full, ArrayList will create a new array with a larger capacity and add the elements in the original array Copy into a new array and add the new elements to the new array.
  3. Expansion strategy: The expansion strategy of ArrayList is to expand the current capacity to the original value every time it is expanded1.5倍. This strategy can ensure that Space utilization can also reduce performance overhead caused by frequent expansion.
  4. Random access: Since ArrayList is implemented internally based on arrays, it supports random access and can directly access elements in the array through indexes. The time complexity isO(1).

In general, ArrayList uses arrays to implement a dynamic array through dynamic expansion, providing efficient random access and the function of dynamic addition and deletion of elements.

2.2 Adding elements: Implementation principle of add() method

In Java, ArrayList'sadd() method is used to add elements to ArrayList.

Its implementation principle is as follows:

  1. When calling the add() method, first check the size and capacity of the current ArrayList (that is, whether the storage space is sufficient).
  2. If the current capacity is not enough, expand the capacity. Typically, a new array is created, the elements in the original array are copied to the new array, and larger storage space is allocated for the new array.
  3. Then the elements to be added are put into the ArrayList's internal array and the size of the ArrayList is updated.
  4. If the addition is successful, returntrue; if the addition fails, returnfalse.

In general, the implementation principle of ArrayList's add() method is to expand the internal array and add elements.

2.3 Capacity expansion mechanism: Implementation principle of ensureCapacity() method

When using ArrayList, if we know the number of elements to be inserted in advance, we can use the ensureCapacity() method to pre-allocate the internal array size. A private method namedensureCapacityInternal() was called. This method will first determine whether the current internal array is large enough to accommodate the new elements. If it is not large enough, it will be expanded:

  1. If the current internal array is empty, it will be directly expanded to the specified capacity.
  2. Otherwise, the capacity of the new array is calculated, which depends on the size of the original array and the expansion factor (default is 1.5 times).
  3. If the new array capacity is smaller than the required capacity, allocate a new array according to the required capacity; otherwise, allocate a new array according to the new array capacity.
  4. Copy the elements in the original array to the new array, and assign the new array to the elementData variable of the ArrayList object.
import java.util.ArrayList;

public class EnsureCapacityExample {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个空的ArrayList
        ArrayList<String> list = new ArrayList<>();

        // 预先设定ArrayList内部数组的容量为20
        list.ensureCapacity(20);

        // 现在,ArrayList的内部数组至少可以容纳20个元素

        // 添加元素到ArrayList
        list.add("Element 1");
        list.add("Element 2");
        list.add("Element 3");

        // ...
    }
}

By using the ensureCapacity() method, we can avoid performance losses caused by frequent capacity expansion and improve program efficiency.

3. Analysis of common operations

3.1 Obtaining elements: Implementation principle of get() method

In Java, the ArrayList'sget() method actually obtains the element at the specified position by calling the index of the array. ArrayList internally maintains an array of Object类型 to store elements.

  • When calling the get() method, ArrayList will use the incoming index as the subscript of the array, directly access the element at the corresponding position in the array, and return the element.
  • Because array access is based on memory address, the time complexity of obtaining elements is O(1), which is constant time complexity.
  • The ArrayListget() method does not copy or reallocate space in the array, so it is very efficient when obtaining elements.
  • Frequent insertion, deletion and other operations involving array expansion may cause performance degradation.

The ArrayListget() method quickly obtains the element at the specified position by directly accessing the underlying array.

3.2 Deleting elements: Implementation principle of remove() method

The ArrayList class in Java is a dynamic array based on array implementation. When we use the remove() method to delete elements from the ArrayList, this method will remove the element at the specified position from the internal array. Remove and move subsequent elements forward one position.

This operation can be achieved through the following steps:

  1. Check whether the subscript of the element to be deleted is out of bounds, and if it is out of bounds, throwIndexOutOfBoundsException exception.
  2. Remove the element to be deleted from the internal array. This process can be achieved through theSystem.arraycopy() method, which can copy the elements in a certain range of the array to another location. superior.
  3. Move subsequent elements forward one position to fill the deleted gaps. This process can also be achieved through the System.arraycopy() method.

ps: ArrayList's remove() method can only remove the first element that is equal to the specified element. If we want to remove all elements that are equal to the specified element, we can do this by looping through the ArrayList and using the remove() method.

3.3 Modify elements: Implementation principle of set() method

ArrayList in Java is an array-based dynamic array implementation, which inherits the AbstractList class and implements the List interface. The set() method is a method in ArrayList, used to replace the element at the specified index position with a new element.

The actual principle is as follows

  1. First,set() method checks whether the passed index is within the range of ArrayList. If the index is less than 0 or greater than or equal to the size of the ArrayList (the value returned by the size() method), an IndexOutOfBoundsException exception will be thrown.
  2. If the index is valid, the specified element is located using the array index and replaced with the new element.
  3. set()Method returns the replaced element.
  4. ArrayList may need to resize the internal array when replacing elements. If the size of the new element does not match the capacity of the current array, ArrayList creates a new array and copies all elements from the old array to the new array.

In short, the implementation principle of ArrayList's set() method is to locate and replace elements through the array index, and may need to dynamically adjust the size of the internal array.

4. Performance analysis

4.1 Time complexity analysis

In Java, ArrayList is a collection class implemented as a dynamic array, which provides random access and fast insertion/removal of elements.

The following are common operations of ArrayList and their time complexity analysis:

  1. Access element (get): Access the element at a specific position through index, the time complexity is O(1).
  2. Insert element (add): Insert an element at the specified position. The average time complexity is O(n). In the worst case, it needs to be inserted after the position. elements are moved backward, the time complexity is O(n).
  3. Delete element (remove): Delete the element at the specified position. The average time complexity isO(n). In the worst case, you need to delete the element after the position. elements are moved forward, the time complexity is O(n).
  4. Find elements (contains): Determine whether the collection contains an element. The average time complexity isO(n), and you need to traverse the entire collection to find it. .
  5. Get the set size (size): Get the number of elements in the set, the time complexity is O(1).

It should be noted that the insertion and deletion operations of ArrayList involve the movement of elements. When the size of the collection is large, these operations may cause performance degradation. If you need to perform frequent insertion and deletion operations, you can consider using LinkedList instead of ArrayList, because LinkedList reduces the time required for insertion and deletion operations. The complexity isO(1).

4.2 Space complexity analysis

The space complexity of ArrayList mainly depends on two factors: the number of elements in the collection and the capacity of the internal array.

  1. Number of elements: The number of elements stored in ArrayList, that is, the size of the collection, will occupy a certain amount of space. Assuming that the number of elements is n, the space complexity is O(n).
  2. Internal array capacity: ArrayList uses a dynamic array internally to store elements. The capacity of the array may be larger than the size of the collection to accommodate elements added in the future. Assuming that the capacity of the array is m, the space complexity is O(m).

It should be noted that the actual space occupied by ArrayList may be more than the number of elements in the collection, because itreserves some extra capacity for subsequent addition of elements< /span>. When the number of elements in the collection approaches or exceeds the capacity of the internal array, ArrayList will automatically expand, reallocate a larger array and copy the original elements to the new array, which may lead to an increase in space complexity.

In actual use, the space complexity can be controlled by adjusting the initial capacity of ArrayList or using the constructor to specify the initial capacity. Usually, if the size of the collection can be estimated, setting an appropriate initial capacity can reduce the frequency of expansion operations and improve performance.

4.3 Comparison with LinkedList

ArrayList and LinkedList are two common collection classes in Java. They both implement the List interface, but they are different in internal implementation and performance characteristics.

The following is a comparison between ArrayList and LinkedList:

  1. Internal reality:
    • ArrayList: Implemented using a dynamic array and internally maintaining a variable-length array to store elements.
    • LinkedList: Implemented using a doubly linked list, which is internally composed of a series of nodes. Each node contains element values ​​and front and rear pointers.
  2. 访问效率
    • ArrayList: Due to the use of array implementation, elements can be accessed directly through indexes, so random access is very efficient and the time complexity is O(1). However, when inserting and deleting elements, elements in the array need to be moved, which is less efficient and has a time complexity of O(n).
    • LinkedList: Inserting and deleting elements is more efficient because only the pointer of the node needs to be adjusted, and the time complexity is O(1). However, when accessing elements randomly, you need to traverse and search in order starting from the head node, which is inefficient and has a time complexity of O(n).
  3. Space occupation:
    • ArrayList: When using a dynamic array, a certain amount of space will be reserved. When the number of elements exceeds the capacity, expansion operation is required. Therefore, there may be additional wasted space.
    • LinkedList: Using the linked list structure, each node needs to store the pointers of the previous and previous nodes in addition to storing elements, so it will slightly increase some extra space.
  4. Applicable scene:
    • ArrayList: Suitable for scenarios with many random access and traversal operations, such as accessing elements based on indexes, traversing collections, etc. But in the case of frequent insertion and deletion of elements, the performance is relatively poor.
    • LinkedList: Suitable for scenarios where elements are frequently inserted and deleted, such as implementing data structures such as queues or stacks. But when accessing elements randomly, the performance is relatively poor.

5. Source code interpretation

5.1 Member variables

Insert image description here

5.2 Construction method

Insert image description here

5.3 trimToSize() method

Insert image description here

5.4 indexOf() method

Insert image description here

5.5 clone() method

Insert image description here

5.6 get() method

Insert image description here

5.7 set() method

Insert image description here

5.8 add() method

Insert image description here

5.9 remove() method

Insert image description here

5.10 addAll() method

Insert image description here

6. Case Analysis and Example Demonstration

6.1 Case analysis

Suppose there is a student management system that needs to store student information, including name, age, gender, etc.

To facilitate management, we can use ArrayList to store student objects.

First define a student class, including three attributes: name, age, and gender:

public class Student {
    
    
    private String name;
    private int age;
    private String gender;

    public Student(String name, int age, String gender) {
    
    
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    // getter 和 setter 方法省略
}

Then create an ArrayList object in the main class and add student information:

import java.util.ArrayList;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 创建ArrayList对象
        ArrayList<Student> list = new ArrayList<>();

        // 添加学生信息
        list.add(new Student("张三", 18, "男"));
        list.add(new Student("李四", 20, "女"));
        list.add(new Student("王五", 19, "男"));

        // 遍历学生信息
        for (Student student : list) {
    
    
            System.out.println("姓名:" + student.getName() + " 年龄:" + student.getAge() + " 性别:" + student.getGender());
        }
    }
}

The result is as follows

姓名:张三 年龄:18 性别:男
姓名:李四 年龄:20 性别:女
姓名:王五 年龄:19 性别:男

6.2 Example demonstration

Demonstrate how to use ArrayList to implement a simple shopping cart program.

First define a product category, including two attributes: name and price:

public class Product {
    
    
    private String name;
    private double price;

    public Product(String name, double price) {
    
    
        this.name = name;
        this.price = price;
    }

    // getter 和 setter 方法省略
}

Then create an ArrayList object in the main class and add product information:

import java.util.ArrayList;
import java.util.Scanner;

public class ShoppingCart {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);

        // 创建ArrayList对象
        ArrayList<Product> cart = new ArrayList<>();

        // 添加商品信息
        cart.add(new Product("可乐", 3.5));
        cart.add(new Product("薯片", 5.0));
        cart.add(new Product("巧克力", 8.0));

        // 输出商品信息
        System.out.println("欢迎来到购物车!");
        for (Product product : cart) {
    
    
            System.out.println(product.getName() + " 价格:" + product.getPrice());
        }

        // 计算总价
        double totalPrice = 0;
        while (true) {
    
    
            System.out.print("请输入要购买的商品编号(输入-1结束):");
            int index = scanner.nextInt();
            if (index == -1) {
    
    
                break;
            }
            Product product = cart.get(index);
            System.out.println("已选择 " + product.getName() + " 价格:" + product.getPrice());
            totalPrice += product.getPrice();
        }
        System.out.println("总价:" + totalPrice);
    }
}

Run the program and the output results are as follows:

欢迎来到购物车!
可乐 价格:3.5
薯片 价格:5.0
巧克力 价格:8.0
请输入要购买的商品编号(输入-1结束):0
已选择 可乐 价格:3.5
请输入要购买的商品编号(输入-1结束):1
已选择 薯片 价格:5.0
请输入要购买的商品编号(输入-1结束):2
已选择 巧克力 价格:8.0
请输入要购买的商品编号(输入-1结束):-1
总价:16.5

盈若安好,便是晴天

Guess you like

Origin blog.csdn.net/qq_51601665/article/details/134966886