Class CopyOnWriteArrayList<E>
CopyOnWriteArrayList là một phiên bản đặc biệt của ArrayList được thiết kế để an toàn khi sử dụng trong môi trường đa luồng (nhiều luồng thực thi cùng lúc). Ý tưởng chính là mỗi khi bạn thay đổi danh sách (thêm, xóa, cập nhật phần tử), nó sẽ tạo ra một bản sao mới của toàn bộ danh sách. Điều này giúp các luồng khác có thể đọc danh sách mà không bị ảnh hưởng bởi các thay đổi đang diễn ra.
Đặc điểm nỗi bật của CopyOnWriteArrayList<E>
↳ An toàn cho đa luồng (Thread-safe): Các thao tác trên danh sách được thực hiện một cách an toàn khi có nhiều luồng truy cập cùng lúc mà không cần sử dụng các cơ chế đồng bộ hóa như synchronized.
↳ Hiệu suất cao cho thao tác đọc: Việc đọc dữ liệu diễn ra nhanh chóng vì không cần chờ đợi hay khóa tài nguyên.
↳ Sử dụng nhiều bộ nhớ: Mỗi khi thêm, xóa, hoặc cập nhật phần tử, toàn bộ danh sách được sao chép, điều này tốn thời gian và bộ nhớ. Do đó, không thích hợp nếu ứng dụng của bạn cần thay đổi dữ liệu thường xuyên.
↳ Iterator không bị lỗi khi danh sách thay đổi: Khi bạn duyệt qua danh sách bằng iterator, nếu danh sách bị thay đổi, iterator sẽ không bị lỗi (như ConcurrentModificationException trong các danh sách thông thường). Tuy nhiên, iterator sẽ không thấy các thay đổi mới nhất; nó chỉ thấy phiên bản của danh sách tại thời điểm nó được tạo ra.
↳ Cho phép giá trị null: Bạn có thể thêm các giá trị null vào danh sách nếu cần thiết.
Khai báo Class CopyOnWriteArrayList<E> trong Java
Để sử dụng Class CopyOnWriteArrayList<E>, bạn cần import gói java.util vào đầu file Java của mình.
Cú pháp câu lệnh import:
Cú Pháp
import java.util.concurrent.CopyOnWriteArrayList;
Cú pháp khai báo Class CopyOnWriteArrayList<E>:
Cú Pháp
public class CopyOnWriteArrayList<E>
extends Object
implements List<E>, RandomAccess, Cloneable, Serializable
Dưới đây là giải thích chi tiết về cú pháp khai báo này:
↳ public: Lớp này có thể được truy cập từ bất kỳ đâu trong chương trình.
↳ class: Từ khóa chỉ ra rằng đây là một lớp (class) trong Java.
↳ CopyOnWriteArrayList<E>: Tên lớp là CopyOnWriteArrayList, và <E> là một tham số kiểu, có nghĩa là lớp này là một lớp generic (tổng quát), có thể chứa các phần tử thuộc bất kỳ kiểu dữ liệu nào.
↳ extends Object: Lớp CopyOnWriteArrayList kế thừa trực tiếp từ lớp Object, lớp cha cơ bản nhất trong Java mà tất cả các lớp khác đều kế thừa mặc định.
↳ implements List<E>: Lớp này cài đặt interface List<E>, tức là nó là một danh sách có thứ tự, cho phép thêm/xóa/truy cập phần tử theo chỉ số.
↳ implements RandomAccess: Giao diện này chỉ ra rằng lớp CopyOnWriteArrayList hỗ trợ truy cập ngẫu nhiên (random access), tức là bạn có thể truy cập trực tiếp đến bất kỳ phần tử nào trong danh sách một cách nhanh chóng.
↳ implements Cloneable: Cho phép đối tượng của lớp này có thể được sao chép (clone) bằng phương thức clone().
↳ implements Serializable: Lớp CopyOnWriteArrayList có thể được tuần tự hóa (serialize), tức là có thể được chuyển đổi thành một chuỗi byte để lưu trữ hoặc truyền qua mạng.
Các constructor của lớp CopyOnWriteArrayList:
Khi làm việc với CopyOnWriteArrayList – một lớp đặc biệt của List được thiết kế cho môi trường đa luồng, việc khởi tạo nó đúng cách là rất quan trọng. CopyOnWriteArrayList có những constructor riêng để bạn có thể tạo một danh sách rỗng, khởi tạo từ một Collection khác, hoặc từ một mảng có sẵn. Dưới đây là chi tiết về các constructor mà bạn có thể sử dụng:
↳ CopyOnWriteArrayList(): Khởi tạo một danh sách rỗng.
↳ CopyOnWriteArrayList(Collection<? extends E> c): Khởi tạo một danh sách chứa các phần tử của bộ sưu tập đã cho, theo thứ tự chúng được trả về bởi iterator của bộ sưu tập.
↳ CopyOnWriteArrayList(E[] toCopyIn): Khởi tạo một danh sách chứa một bản sao của mảng đã cho.
Ví dụ
// Khởi tạo một danh sách rỗng
CopyOnWriteArrayList<String> emptyList = new CopyOnWriteArrayList<>();
// Khởi tạo một danh sách từ một ArrayList
List<String> colors = new ArrayList<>();
CopyOnWriteArrayList<String> colorList = new CopyOnWriteArrayList<>(colors);
// Khởi tạo một danh sách từ một mảng
String[] fruits = {"Táo", "Chuối", "Cam"};
CopyOnWriteArrayList<String> fruitList = new CopyOnWriteArrayList<>(fruits);
Lưu ý:
↳ CopyOnWriteArrayList là một danh sách không đồng bộ (thread-safe) sử dụng kỹ thuật copy-on-write. Điều này có nghĩa là khi một phương thức thay đổi danh sách được gọi, danh sách sẽ tạo ra một bản sao mới và thực hiện thay đổi trên bản sao đó, để tránh xung đột khi nhiều luồng truy cập danh sách cùng lúc.
↳ Khi tạo một CopyOnWriteArrayList, các phần tử ban đầu sẽ được sao chép vào danh sách mới. Thay đổi bộ sưu tập gốc sẽ không ảnh hưởng đến CopyOnWriteArrayList đã được tạo.
Các phương thức chính trong lớp CopyOnWriteArrayList
Lớp CopyOnWriteArrayList cung cấp các phương thức để thao tác với một danh sách không đồng bộ. Bạn có thể thêm, xóa, lấy, kiểm tra và duyệt qua các phần tử trong danh sách một cách thread-safe. Dưới đây là danh sách tất cả các phương thức của Class CopyOnWriteArrayList<E> trong Java:
↳ boolean add(E e): Thêm phần tử e vào cuối danh sách.
↳ void add(int index, E element): Chèn phần tử element vào vị trí index trong danh sách.
↳ boolean addAll(Collection<? extends E> c): Thêm tất cả các phần tử trong collection c vào cuối danh sách.
↳ boolean addAll(int index, Collection<? extends E> c): Chèn tất cả các phần tử trong collection c vào vị trí index trong danh sách.
↳ int addAllAbsent(Collection<? extends E> c): Thêm tất cả các phần tử trong collection c mà chưa có trong danh sách này vào cuối danh sách, theo thứ tự chúng được trả về bởi iterator của collection đã cho.
↳ boolean addIfAbsent(E e): Thêm phần tử e vào cuối danh sách nếu nó chưa có.
↳ void clear(): Xóa tất cả các phần tử khỏi danh sách.
↳ Object clone(): Trả về một bản sao nông của danh sách này.
↳ boolean contains(Object o): Trả về true nếu danh sách này chứa phần tử đã cho.
↳ boolean containsAll(Collection<?> c): Trả về true nếu danh sách này chứa tất cả các phần tử của collection đã cho.
↳ boolean equals(Object o): So sánh đối tượng đã cho với danh sách này để kiểm tra xem chúng có bằng nhau hay không.
↳ void forEach(Consumer<? super E> action): Thực hiện hành động đã cho cho mỗi phần tử của Iterable cho đến khi tất cả các phần tử đã được xử lý hoặc hành động ném ra một ngoại lệ.
↳ get(int index): Lấy phần tử tại vị trí index trong danh sách.
↳ int hashCode(): Trả về giá trị mã băm cho danh sách này.
↳ int indexOf(E e, int index): Trả về chỉ số lần xuất hiện đầu tiên của phần tử e trong danh sách này, bắt đầu từ vị trí index, hoặc trả về -1 nếu phần tử không được tìm thấy.
↳ int indexOf(Object o): Trả về chỉ số lần xuất hiện đầu tiên của phần tử đã cho trong danh sách này, hoặc -1 nếu danh sách này không chứa phần tử đã cho.
↳ boolean isEmpty(): Trả về true nếu danh sách này không chứa phần tử nào.
↳ Iterator<E> iterator(): Trả về một iterator duyệt qua các phần tử trong danh sách này theo thứ tự đúng.
↳ int lastIndexOf(E e, int index): Trả về chỉ số lần xuất hiện cuối cùng của phần tử e trong danh sách này, bắt đầu từ vị trí index, hoặc trả về -1 nếu phần tử không được tìm thấy.
↳ int lastIndexOf(Object o): Trả về chỉ số lần xuất hiện cuối cùng của phần tử đã cho trong danh sách này, hoặc -1 nếu danh sách này không chứa phần tử đã cho.
↳ ListIterator<E> listIterator(): Trả về một list iterator duyệt qua các phần tử trong danh sách này theo thứ tự đúng.
↳ ListIterator<E> listIterator(int index): Trả về một list iterator duyệt qua các phần tử trong danh sách này theo thứ tự đúng, bắt đầu từ vị trí index đã cho.
↳ remove(int index): Xóa phần tử tại vị trí index trong danh sách này.
↳ boolean remove(Object o): Xóa phần tử đầu tiên xuất hiện của phần tử đã cho khỏi danh sách này, nếu có.
↳ boolean removeAll(Collection<?> c): Xóa khỏi danh sách này tất cả các phần tử có trong bộ sưu tập đã cho.
↳ boolean removeIf(Predicate<? super E> filter): Xóa tất cả các phần tử của collection này thỏa mãn điều kiện filter đã cho.
↳ void replaceAll(UnaryOperator<E> operator): Thay thế mỗi phần tử của danh sách này bằng kết quả của việc áp dụng toán tử lên phần tử đó.
↳ boolean retainAll(Collection<?> c): Giữ lại chỉ những phần tử trong danh sách này có trong bộ sưu tập đã cho.
↳ set(int index, E element): Thay thế phần tử tại vị trí index trong danh sách này bằng phần tử đã cho.
↳ int size(): Trả về số lượng phần tử trong danh sách này.
↳ void sort(Comparator<? super E> c): Sắp xếp danh sách này theo thứ tự được quy định bởi bộ so sánh đã cho.
↳ Spliterator<E> spliterator(): Trả về một Spliterator duyệt qua các phần tử trong danh sách này.
↳ List<E>subList(int fromIndex, int toIndex): Trả về một view của phần của danh sách này nằm giữa fromIndex (bao gồm) và toIndex (không bao gồm).
↳ Object[] toArray(): Trả về một mảng chứa tất cả các phần tử trong danh sách này theo thứ tự đúng (từ phần tử đầu tiên đến phần tử cuối cùng).
↳ <T> T[] toArray(T[] a): Trả về một mảng chứa tất cả các phần tử trong danh sách này theo thứ tự đúng (từ phần tử đầu tiên đến phần tử cuối cùng); kiểu thời gian chạy của mảng được trả về là kiểu của mảng đã cho.
↳ String toString(): Trả về một chuỗi biểu diễn danh sách này.
Dưới đây là một ví dụ đơn giản về cách sử dụng CopyOnWriteArrayList trong Java. Ví dụ này minh họa cách thêm, xóa, và truy xuất phần tử trong CopyOnWriteArrayList.
Ví dụ: Example.java
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class Example {
public static void main(String[] args) {
// Tạo một CopyOnWriteArrayList
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// Thêm phần tử vào danh sách
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// In danh sách
System.out.println("Danh sách ban đầu: " + list);
// Thêm một phần tử vào danh sách
list.add("Date");
System.out.println("Danh sách sau khi thêm phần tử: " + list);
// Xóa một phần tử khỏi danh sách
list.remove("Banana");
System.out.println("Danh sách sau khi xóa phần tử: " + list);
// Truy xuất và in từng phần tử bằng iterator
System.out.println("Danh sách sử dụng iterator:");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// Truy xuất và in từng phần tử bằng vòng lặp for-each
System.out.println("Danh sách sử dụng vòng lặp for-each:");
for (String item : list) {
System.out.println(item);
}
}
}
Kết quả của chương trình là:
Danh sách sau khi thêm phần tử: [Apple, Banana, Cherry, Date]
Danh sách sau khi xóa phần tử: [Apple, Cherry, Date]
Danh sách sử dụng iterator:
Apple
Cherry
Date
Danh sách sử dụng vòng lặp for-each:
Apple
Cherry
Date
Như vậy, chúng ta đã khám phá các phương thức chính của CopyOnWriteArrayList. Đây là một lựa chọn tuyệt vời khi bạn cần một danh sách an toàn cho đa luồng (thread-safe), đặc biệt là trong các trường hợp có nhiều thao tác đọc và ít thao tác ghi. Hiểu rõ cách CopyOnWriteArrayList hoạt động, với cơ chế "copy-on-write" độc đáo của nó, sẽ giúp bạn xây dựng các ứng dụng đa luồng mạnh mẽ và ổn định hơn trong Java.