In-depth understanding of the List collection in Java

introduction

The List collection in Java is a commonly used data structure that provides an ordered and repeatable collection of elements. In development, we often need to use List to store and manipulate a set of data. This article will give an in-depth introduction to the characteristics of the List collection in Java, common operation methods and some usage skills to help readers better understand and apply the List collection.

An overview of the List collection:

What is a List collection?

In Java, a List collection is a commonly used data structure used to store a set of ordered, repeatable elements. It is part of the Java Collections Framework, located under the java.util package.

The characteristics of List: ordered and repeatable.

  1. Orderedness: The elements in a List are stored in the order in which they were added, and each element can be accessed through an index. This means that when we iterate over the List, the elements are in the same order as they were added.
  2. Repeatability: List allows storing the same element multiple times. That is, duplicate elements can be added to the List, and they can maintain their respective positions and orders.

Comparison of List and array.

Common List implementation classes:

  1. ArrayList: Dynamic array implementation, suitable for lookup and random access operations.
  2. LinkedList: Linked list implementation, suitable for frequent insertion and deletion operations.
  3. Vector: A thread-safe dynamic array with worse performance than ArrayList.

Common operation methods of List collection:

Adding elements: use of the add() method.
Get elements: use of get() method and index.
Removing elements: remove() method and application of iterators.
Traversing elements: a comparison of for loops, iterators, and foreach loops.
Determine whether an element exists: the difference between the contains() method and the equals() method.

Sorting and comparison of List collections:

  1. Size variability:
    The size of an array is determined when it is created, and the size cannot be changed directly. If you need to change the size of the array, you need to create a new array and copy the elements into the new array.
    List is an interface, and there are many implementation classes (such as ArrayList, LinkedList, etc.), which have variable sizes. The size of the List can be changed by adding, removing or replacing elements.
  2. Data types:
    Arrays can contain elements of any type, including primitive types and object types.
    List can only contain elements of object type, not primitive types. If you need to store basic types, you need to use their corresponding wrapper classes.
  3. Traversing and accessing elements:
    Arrays can use indexes to directly access elements, and traversing arrays through loops is also more efficient.
    List elements are accessed and traversed through iterators or using indexes. Although lists can also use indexes, using iterators is more common than arrays.
  4. Functionality and flexibility:
    An array is a simple data structure that provides basic access and manipulation methods, such as getting length, copying, etc. But it does not provide advanced functions such as built-in addition, deletion, modification and query.
    List is an interface that provides more operation methods, such as adding, deleting, replacing, searching, etc. It is convenient to insert, delete and find elements.
  5. Memory management:
    An array is a set of elements stored contiguously in memory. Once created, their size is fixed and cannot be dynamically adjusted.
    Lists are usually implemented as linked lists or dynamic arrays whose size can expand or contract as needed.
    In general, if you need a fixed-size, more performant data structure, use arrays. If you need a variable-sized data structure that provides more manipulation methods, you can use List. The choice of which data structure to use depends on the specific needs and usage scenarios.

Sorting List: Collections.sort() method and use of Comparator.

Collections.sort()

In Java, a List can be sorted using the Collections.sort() method. This method sorts the elements using the default natural ordering. If the elements in the List are primitive types or object types that implement the Comparable interface, they should support natural ordering.

The following is a sample code for sorting a List using the Collections.sort() method:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ListSortExample {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);

        System.out.println("Before sorting: " + numbers);

        Collections.sort(numbers);

        System.out.println("After sorting: " + numbers);
    }
}

output result

Before sorting: [5, 2, 8, 1]
After sorting: [1, 2, 5, 8]

Object comparison: the use of Comparable interface and Comparator interface.

In some cases, it may be necessary to use custom sorting logic. To do this, you can use the Comparator interface to create a comparator and pass it to the Collections.sort() method. Comparators define comparison rules between elements.

The following is an example code for custom sorting using Comparator:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ListSortExample {
    
    
    public static void main(String[] args) {
    
    
        List<String> names = new ArrayList<>();
        names.add("John");
        names.add("Alice");
        names.add("Bob");
        names.add("David");

        System.out.println("Before sorting: " + names);

        // 使用自定义的比较器按照字符串长度排序
        Collections.sort(names, new LengthComparator());

        System.out.println("After sorting: " + names);
    }

    // 自定义比较器
    static class LengthComparator implements Comparator<String> {
    
    
        @Override
        public int compare(String s1, String s2) {
    
    
            return Integer.compare(s1.length(), s2.length());
        }
    }
}

output result

Before sorting: [John, Alice, Bob, David]
After sorting: [Bob, John, Alice, David]

In the above example, the LengthComparator is used to customize the comparator to sort the elements according to the length of the string.
By using Comparator, we can flexibly sort the elements in the List according to our own needs.

Performance optimization and precautions for List collections:

Initialize the capacity of List

When initializing a List, you can optionally specify its initial capacity. The initial capacity refers to the size of the internal array allocated by the List when it is created. Specifying an appropriate initial capacity can improve performance by avoiding frequent internal array reallocation and copy operations.

For situations where the number of elements to be stored is known or estimated, a reasonable initial capacity can be selected based on experience. In general, the following rules can be used as a reference:

If the number of elements is known, the initial capacity can be specified directly based on the number. For example, if you know that the List will contain 100 elements, you can use new ArrayList<>(100) to initialize an ArrayList with a capacity of 100.

If the number of elements is uncertain, but a range of numbers is roughly estimated, an initial capacity close to that range can be chosen. For example, if the estimated number of elements is between 100 and 1000, you can choose new ArrayList<>(500).

If the number of elements is completely uncertain, you can choose a small initial capacity, such as 10 or 20. List will automatically expand capacity as needed, so a small initial capacity will not cause performance problems, but will save some memory space.

It should be noted that the initial capacity does not limit the size of the List, and the List can grow dynamically. If the initial capacity is not enough to accommodate the added elements, List will automatically increase the capacity to accommodate more elements.

All in all, choosing an appropriate initial capacity depends on the estimate of the number of elements and the needs of the application. To make a trade-off between performance and memory consumption, you can choose an appropriate initial capacity according to the actual situation. If the initial capacity is too small, it may cause frequent internal array reallocation operations, and if the initial capacity is too large, memory may be wasted.

Use Iterator to traverse collections

In Java, iterating over a List using an Iterator is a common practice for several reasons:

Unified traversal method: Using Iterator can provide a unified way to traverse different types of List, whether it is ArrayList, LinkedList or other classes that implement the List interface, you can use the same traversal method to make the code more consistent and readability.

Support for concurrent modification: Iterator provides safe concurrent modification support. In the process of traversing the List using Iterator, if other threads modify the List, ConcurrentModificationException will not be thrown. This is because Iterator will maintain a modification counter during the traversal process. When the List is modified, it will check whether the value of the counter is consistent, and if not, an exception will be thrown.

Elements can be deleted: Iterator provides the remove() method, which can safely delete the currently iterated element during traversal without destroying the traversal state. This is a feature that the remove() method of the List interface does not have.
The following is a sample code for traversing a List using Iterator:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListIteratorExample {
    
    
    public static void main(String[] args) {
    
    
        List<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");

        Iterator<String> iterator = fruits.iterator();
        while (iterator.hasNext()) {
    
    
            String fruit = iterator.next();
            System.out.println(fruit);
        }
    }
}

Avoid frequent insert and delete operations.

In Java, the performance of List implementation classes (such as ArrayList and LinkedList) for frequent insertion and deletion operations may vary, but it is generally recommended to avoid frequent insertion and deletion operations, especially on large data sets. Here are a few reasons:

  1. Memory and performance overhead: Insertion and deletion operations may involve reallocation and copying of internal arrays or linked lists. These operations may result in a large number of memory allocation and copy operations, causing additional memory and performance overhead.

  2. Time complexity: In ArrayList, insertion and deletion operations involve the backward and forward movement of elements, resulting in an average time complexity of O(n). In LinkedList, the average time complexity of insertion and deletion operations is O(1), but more memory overhead is required.

  3. Data Consistency and Iterator Invalidation: Frequent insertion and deletion operations may invalidate iterators or produce inconsistent results. When the List is modified during the traversal of the iterator, or concurrent modification is performed in a multi-threaded environment, ConcurrentModificationException exceptions or indeterminate results may occur.

If you need frequent insertion and deletion operations, you can consider using other data structures, such as LinkedList. LinkedList performs better for insertion and deletion operations because it is implemented based on a linked list. But ArrayList is usually more efficient when it comes to random access and traversal of elements.

In practical applications, it is necessary to select an appropriate data structure according to specific needs and scenarios. If you need to perform frequent insertion and deletion operations, you can evaluate the performance characteristics of different data structures and choose the most suitable implementation class to improve efficiency.

Application scenarios and actual cases of List collection:

Data storage and processing.
Implement data structures such as stacks and queues.
Implement search and filter functionality.

in conclusion

Through the introduction of this article, we have a deeper understanding of the List collection in Java. As a commonly used data structure, List has the characteristics of order and repeatability, and is suitable for data storage and operation in various scenarios. Mastering the common methods and techniques of List collection can improve development efficiency and code quality. I hope this article can help readers better understand and apply the List collection in Java.

Guess you like

Origin blog.csdn.net/baidu_29609961/article/details/130987630