- 1 1. Giới thiệu
- 2 2. List là gì?
- 3 3. Sử dụng cơ bản của List
- 4 4. Ví dụ về các thao tác List
- 5 5. Sự khác nhau và cách sử dụng ArrayList và LinkedList
- 6 6. Sử dụng Nâng cao của List
- 7 7. Lỗi Thường gặp và Giải pháp của Chúng
- 8 8. Kết Luận
- 9 Câu Hỏi Thường Gặp (FAQ)
- 9.1 Q2. Nên dùng ArrayList hay LinkedList?
- 9.2 Q3. Tôi có thể lưu các kiểu nguyên thủy (như int hoặc double) trong List không?
- 9.3 Q4. Làm sao để sắp xếp các phần tử trong List?
- 9.4 Q5. Tôi nên làm gì nếu muốn quản lý các phần tử mà không có trùng lặp?
- 9.5 Q6. Làm sao để xóa tất cả các phần tử khỏi một List?
- 9.6 Q7. Các thao tác được sử dụng thường xuyên nhất trong List là gì?
1. Giới thiệu
List trong Java quan trọng như thế nào?
Trong lập trình Java, “List” là một cấu trúc dữ liệu xuất hiện rất thường xuyên. Đặc biệt trong các tình huống bạn muốn quản lý nhiều giá trị cùng nhau, nó linh hoạt và dễ sử dụng hơn mảng, khiến nó được đánh giá cao trong nhiều tình huống thực tế.
“List” là một giao diện cốt lõi trong Java Collections Framework và cung cấp cơ chế để xử lý các tình huống khác nhau thông qua các lớp triển khai khác nhau như ArrayList và LinkedList. Khả năng thực hiện các hoạt động như thêm, xóa, tìm kiếm và cập nhật dữ liệu một cách trực quan là một trong những lý do tại sao List được ưa chuộng.
Mục đích và Đối tượng mục tiêu của Bài viết này
Bài viết này sẽ giải thích một cách có hệ thống “Java List” từ cơ bản đến nâng cao một cách dễ hiểu cho người mới bắt đầu. Đối tượng mục tiêu chính là như sau:
- Những người mới bắt đầu học Java và chưa chắc chắn về cách sử dụng List
- Những người muốn hiểu rõ sự khác biệt giữa Array và List
- Những người đang gặp khó khăn trong việc chọn giữa ArrayList và LinkedList
- Những người muốn ôn lại kiến thức cơ bản trước khi sử dụng List trong thực tế
Đến khi bạn đọc xong bài viết này, mục tiêu của chúng tôi là bạn sẽ có sự hiểu biết vững chắc về các khái niệm cơ bản, phương pháp triển khai và các hoạt động cụ thể của List trong Java, cho phép bạn mã hóa một cách tự tin.
Từ chương tiếp theo, chúng tôi sẽ bắt đầu bằng cách giải thích phần cơ bản, “List là gì?”, từng bước một.
2. List là gì?
Tổng quan và Đặc điểm của List
“List” trong Java là một giao diện bộ sưu tập giữ các phần tử theo thứ tự. Đặc điểm lớn nhất của nó là giữ nguyên thứ tự thêm phần tử và có thể truy cập các phần tử riêng lẻ bằng chỉ số (bắt đầu từ 0).
List được cung cấp như một phần của Collections Framework và có các đặc điểm sau:
- Cho phép các phần tử trùng lặp
- Có thể lấy, cập nhật và xóa phần tử bằng cách chỉ định chỉ số
- Có thể tăng hoặc giảm số lượng phần tử một cách động (khác với mảng, nó không có kích thước cố định)
Điều này cho phép thao tác dữ liệu linh hoạt và được sử dụng rất thường xuyên trong công việc thực tế.
Sự khác biệt với Array
Trong Java, mảng (như int[] hoặc String[]) cũng tồn tại như một phương tiện để giữ nhiều giá trị, nhưng có một số khác biệt so với List.
| Comparison Item | Array | List |
|---|---|---|
| Changing number of elements | Not possible (fixed-size) | Possible (can increase/decrease dynamically) |
| Provided functionality | Minimal operations (indexed access, length retrieval) | Rich methods (add, remove, contains, etc.) |
| Type | Can handle primitive types | Object types only (wrapper classes required) |
| Type safety | Arrays checked at compile time | Can strictly specify type with Generics |
Như vậy, List là một bộ sưu tập linh hoạt và giàu tính năng hơn, khiến nó thực tế hơn mảng trong nhiều tình huống.
Giao diện List và các Lớp Triển khai của nó
Khi sử dụng List trong Java, bạn thường khai báo biến bằng giao diện List và tạo instance với một lớp cụ thể (lớp triển khai). Các lớp triển khai đại diện là như sau:
- ArrayList Cấu trúc tương tự mảng, cho phép truy cập nhanh. Mạnh về tìm kiếm dữ liệu và truy cập ngẫu nhiên.
- LinkedList Được triển khai với cấu trúc danh sách liên kết đôi. Nhanh cho việc chèn và xóa, phù hợp cho danh sách nơi các hoạt động thường xuyên.
- Vector Tương tự ArrayList nhưng hơi nặng hơn vì an toàn luồng. Không được sử dụng nhiều ngày nay.
Nói chung, ArrayList là phổ biến nhất trừ khi có lý do đặc biệt. Tốt để chọn cái phù hợp dựa trên so sánh hiệu suất được mô tả sau, tùy thuộc vào trường hợp sử dụng.
3. Sử dụng cơ bản của List
Phần này giải thích các hoạt động cơ bản khi sử dụng List trong Java từng bước. Ở đây, chúng tôi sẽ chủ yếu sử dụng ArrayList làm ví dụ để giới thiệu các hoạt động đại diện của List.
Khai báo và Khởi tạo List
Đầu tiên, hãy xem xét khai báo và khởi tạo cơ bản của List sử dụng ArrayList.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
}
}
Việc khai báo một biến với giao diện List và khởi tạo nó bằng ArrayList là thực hành phổ biến. Generics được dùng để chỉ định kiểu dữ liệu sẽ lưu trữ (ở đây là String).
Thêm phần tử (add)
Để thêm phần tử vào List, sử dụng phương thức add().
fruits.add("apple");
fruits.add("banana");
fruits.add("orange");
Điều này sẽ thêm ba phần tử vào List theo thứ tự. List giữ nguyên thứ tự thêm vào.
Lấy phần tử (get)
Để lấy một phần tử tại chỉ mục xác định, sử dụng get(int index).
System.out.println(fruits.get(0)); // "apple" will be displayed
Lưu ý rằng chỉ mục bắt đầu từ 0.
Cập nhật phần tử (set)
Để cập nhật một phần tử ở vị trí nào đó, sử dụng set(int index, E element).
fruits.set(1, "grape"); // The second element "banana" is replaced with "grape"
Xóa phần tử (remove)
Bạn cũng có thể xóa phần tử bằng chỉ mục cụ thể hoặc bằng chính phần tử đó.
fruits.remove(0); // Removes the first element
fruits.remove("orange"); // Removes "orange" (only the first match)
Lấy kích thước List (size)
Số lượng phần tử hiện tại có thể lấy bằng phương thức size().
System.out.println(fruits.size()); // Returns 2, etc.
Kiểm tra sự tồn tại của phần tử (contains)
Để kiểm tra một phần tử cụ thể có nằm trong List hay không, sử dụng contains().
if (fruits.contains("grape")) {
System.out.println("grape is present");
}
Tóm tắt: Các thao tác cơ bản thường dùng trên List
| Operation | Method Example | Description |
|---|---|---|
| Addition | add("element") | Adds to the end |
| Retrieval | get(index) | References an element |
| Update | set(index, new element) | Changes the element at the specified position |
| Removal | remove(index/element) | Removes the specified element |
| Get Size | size() | Gets the number of elements |
| Check Existence | contains("element") | Checks if a specific element exists |
4. Ví dụ về các thao tác List
Trong chương này, chúng ta sẽ giới thiệu các ví dụ thực tế về thao tác List trong Java. Có rất nhiều trường hợp bạn muốn xử lý các phần tử trong danh sách một cách tuần tự, và ở đây chúng ta sẽ đề cập đến các phương pháp tiêu biểu bằng vòng for, vòng for‑enhanced và Stream API.
Duyệt bằng vòng for
Cách cơ bản nhất là lấy các phần tử bằng chỉ mục trong một vòng for.
List<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("banana");
fruits.add("orange");
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i));
}
Phương pháp này cho phép kiểm soát chi tiết thông qua chỉ mục. Ví dụ, nó hiệu quả khi bạn muốn xử lý chỉ các phần tử ở vị trí chẵn.
Duyệt bằng vòng for‑enhanced (for‑each)
Nếu bạn muốn xử lý tất cả các phần tử một cách tuần tự mà không cần quan tâm tới chỉ mục, vòng for‑enhanced rất tiện lợi.
for (String fruit : fruits) {
System.out.println(fruit);
}
Cú pháp đơn giản và dễ đọc, làm cho nó trở thành một trong những phương pháp được sử dụng nhiều nhất. Điều này đủ cho các xử lý đơn giản.
Duyệt bằng Lambda Expressions và Stream API
Kể từ Java 8, bạn cũng có thể sử dụng cú pháp kết hợp Stream API và biểu thức lambda.
fruits.stream().forEach(fruit -> System.out.println(fruit));
Ưu điểm của cách viết này là có thể xâu chuỗi nhiều thao tác lại với nhau. Ví dụ, bạn có thể dễ dàng lọc và sau đó in ra các phần tử dựa trên tiêu chí cụ thể.
fruits.stream()
.filter(fruit -> fruit.contains("a"))
.forEach(System.out::println);
Trong ví dụ này, nó chỉ in ra các loại trái cây chứa chữ “a”. Đây là đề xuất đặc biệt cho những ai muốn làm quen với lập trình theo phong cách hàm.
Lựa chọn phương pháp phù hợp
| Method | Advantages | Suitable Situations |
|---|---|---|
| Regular for loop | Allows index control | Processing that requires element numbers |
| Enhanced for loop | Simple and easy to read syntax | Simple iteration processing |
| Stream API | Strong for conditional and chained processing | When combining filtering, mapping, and reduction |
5. Sự khác nhau và cách sử dụng ArrayList và LinkedList
Các lớp tiêu biểu triển khai giao diện List của Java là ArrayList và LinkedList. Cả hai đều có thể được dùng như một List theo cùng một cách, nhưng chúng có sự khác nhau về cấu trúc nội bộ và đặc tính hiệu năng, vì vậy việc sử dụng chúng một cách thích hợp trong từng tình huống là rất quan trọng.
Đặc điểm và trường hợp sử dụng phù hợp của ArrayList
ArrayList bên trong sử dụng một mảng động (dynamic array).
Đặc điểm chính:
- Truy cập ngẫu nhiên (dựa trên chỉ số) rất nhanh
- Thêm phần tử vào cuối danh sách nhanh (trung bình O(1))
- Chèn và xóa ở giữa chậm hơn (O(n))
Tình huống phù hợp:
- Tình huống tìm kiếm (
get()) thường xuyên - Tình huống số lượng phần tử có thể dự đoán trước một phần
- Xử lý nơi thêm/xóa phần tử tối thiểu, tập trung vào đọc
List<String> list = new ArrayList<>();
Đặc điểm và Tình huống sử dụng phù hợp của LinkedList
LinkedList được triển khai với cấu trúc danh sách liên kết đôi.
Đặc điểm chính:
- Nhanh cho việc thêm và xóa phần tử (đặc biệt ở đầu hoặc cuối)
- Truy cập ngẫu nhiên (
get(index)) chậm (O(n)) - Tiêu thụ bộ nhớ cao hơn một chút so với ArrayList
Tình huống phù hợp:
- Tình huống phần tử được chèn hoặc xóa thường xuyên (đặc biệt ở đầu hoặc giữa)
- Khi muốn sử dụng như Queue hoặc Stack
- Khi tập trung vào lặp và không cần truy cập chỉ số
List<String> list = new LinkedList<>();
So sánh Hiệu suất
Bảng sau cho thấy độ phức tạp thời gian lý thuyết (ký hiệu Big O) cho các hoạt động thường dùng.
| Operation | ArrayList | LinkedList |
|---|---|---|
get(int index) | O(1) | O(n) |
add(E e) (at the end) | O(1) | O(1) |
add(int index, E e) | O(n) | O(n) |
remove(int index) | O(n) | O(n) |
| Iteration | O(n) | O(n) |
* Thời gian xử lý thực tế cũng có thể bị ảnh hưởng bởi kích thước dữ liệu, tối ưu hóa JVM, v.v.

Điểm khác biệt cho Sử dụng Thực tế
- Nếu coi dữ liệu như một danh sách và truy cập bằng chỉ số, sử dụng ArrayList
- Nếu chèn/xóa ở đầu hoặc giữa thường xuyên, sử dụng LinkedList
- Đối với xử lý nhạy cảm về hiệu suất, luôn benchmark và xác minh
6. Sử dụng Nâng cao của List
Ở đây, chúng tôi sẽ giới thiệu các kỹ thuật nâng cao để sử dụng List của Java tiện lợi hơn. List có thể thực hiện các hoạt động đa dạng không chỉ như một bộ sưu tập dữ liệu đơn giản mà còn qua sắp xếp, xáo trộn, lọc, biến đổi, v.v.
Sắp xếp một List (Collections.sort)
Sử dụng Collections.sort(), bạn có thể sắp xếp các phần tử trong List theo thứ tự tăng dần. Các phần tử phải triển khai giao diện Comparable.
import java.util.*;
List<String> fruits = new ArrayList<>();
fruits.add("banana");
fruits.add("apple");
fruits.add("orange");
Collections.sort(fruits);
System.out.println(fruits); // [apple, banana, orange]
Sắp xếp theo thứ tự tùy chỉnh (sử dụng Comparator)
fruits.sort(Comparator.reverseOrder()); // Sorts in descending order
Xáo trộn một List (Collections.shuffle)
Để sắp xếp lại các phần tử một cách ngẫu nhiên, bạn có thể sử dụng Collections.shuffle().
Collections.shuffle(fruits);
System.out.println(fruits); // [banana, orange, apple] (example)
Điều này hữu ích khi bạn muốn một bộ bài cho trò chơi hoặc thứ tự hiển thị ngẫu nhiên.
Lọc sử dụng Stream API (filter)
Sử dụng Stream từ Java 8 trở đi, bạn có thể viết code ngắn gọn để trích xuất chỉ các phần tử khớp với điều kiện.
List<String> filtered = fruits.stream()
.filter(fruit -> fruit.contains("a"))
.collect(Collectors.toList());
System.out.println(filtered); // [apple, banana, orange] (depending on original content and filter)
Biến đổi sử dụng Stream API (map)
Để biến đổi các phần tử thành định dạng khác, sử dụng map().
List<Integer> lengths = fruits.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths); // Lengths of each fruit name [5, 6, 6] etc.
map() là công cụ mạnh mẽ cho chuyển đổi định dạng dữ liệu và tiền xử lý.
Tóm tắt Các Hoạt động Nâng cao
| Operation | Usage Example | Main Use Cases |
|---|---|---|
| Sort | Collections.sort(list) | Sort in ascending order |
| Shuffle | Collections.shuffle(list) | Randomize the order of elements |
| Filter | stream().filter(...).collect() | Extract only elements that match a condition |
| Transform | stream().map(...).collect() | Transform the type or value of elements |
7. Lỗi Thường gặp và Giải pháp của Chúng
Khi làm việc với Lists trong Java, một trong những thứ mà người mới bắt đầu thường gặp phải là “exceptions (lỗi)”. Ở đây, chúng tôi sẽ giải thích cụ thể các lỗi đại diện xảy ra thường xuyên, nguyên nhân và cách giải quyết chúng.
IndexOutOfBoundsException
Nguyên nhân:
Xảy ra khi cố gắng truy cập một chỉ số không tồn tại.
List<String> list = new ArrayList<>();
list.add("apple");
System.out.println(list.get(1)); // Error: Index 1 out of bounds
Giải pháp:
Kiểm tra kích thước trước khi truy cập hoặc kiểm soát truy cập bằng cách phân nhánh điều kiện để đảm bảo chỉ số hợp lệ.
if (list.size() > 1) {
System.out.println(list.get(1));
}
NullPointerException
Nguyên nhân:
Xảy ra khi gọi phương thức trên một List hoặc phần tử List là null.
List<String> list = null;
list.add("apple"); // NullPointerException occurs
Giải pháp:
Kiểm tra trước rằng biến không phải là null, hoặc sử dụng Optional, v.v.
if (list != null) {
list.add("apple");
}
Ngoài ra, hãy cẩn thận về việc quên khởi tạo:
List<String> list = new ArrayList<>(); // Correct initialization
ConcurrentModificationException
Nguyên nhân:
Xảy ra khi List bị sửa đổi trực tiếp trong khi lặp qua nó bằng vòng lặp for-each hoặc Iterator.
for (String fruit : list) {
if (fruit.equals("banana")) {
list.remove(fruit); // ConcurrentModificationException
}
}
Giải pháp:
Sử dụng Iterator để loại bỏ các phần tử một cách an toàn, hoặc sử dụng các phương thức như removeIf().
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("banana")) {
it.remove(); // Safe removal
}
}
Hoặc, ngắn gọn hơn từ Java 8 trở đi:
list.removeIf(fruit -> fruit.equals("banana"));
Các Điểm Khác Cần Lưu Ý
- Kiểm tra rằng List không phải là null
- Việc khai báo biến nhưng không sử dụng là rất phổ biến. Khởi tạo là thiết yếu.
- Hiểu rằng chỉ số bắt đầu từ 0
- Người mới bắt đầu thường nhầm lẫn nghĩ “phần tử đầu tiên là chỉ số 1”.
Tóm Tắt Các Biện Pháp Đối Phó Lỗi
| Error Name | Primary Cause | Example Solutions |
|---|---|---|
| IndexOutOfBoundsException | Accessing a non-existent index | Check length with size() |
| NullPointerException | List or element is null | Don’t forget initialization, perform null checks |
| ConcurrentModificationException | Directly modifying the List during iteration | Operate with Iterator or utilize removeIf() |
8. Kết Luận
Ôn Tập Các Kiến Thức Cơ Bản Về Java List
Trong bài viết này, chúng tôi đã giải thích từ cơ bản đến nâng cao về List trong Java từng bước một. List được sử dụng đặc biệt thường xuyên trong các bộ sưu tập Java và là công cụ quan trọng để xử lý dữ liệu một cách linh hoạt.
Đầu tiên, sau khi hiểu List là gì, chúng tôi đã học các điểm sau:
- List là một bộ sưu tập có thứ tự cho phép trùng lặp và hỗ trợ các hoạt động chỉ số
- Có các lớp triển khai đại diện như ArrayList và LinkedList , mỗi lớp có đặc điểm và trường hợp sử dụng khác nhau
- Làm chủ các hoạt động cơ bản (thêm, lấy, cập nhật, xóa, tìm kiếm) cho phép thao tác dữ liệu linh hoạt
- Xử lý lặp phù hợp với tình huống , chẳng hạn như vòng lặp for, vòng lặp for nâng cao, và Stream API
- Hỗ trợ các hoạt động nâng cao như sắp xếp, lọc, và biến đổi
- Hiểu các lỗi phổ biến, nguyên nhân và giải pháp giúp ngăn ngừa vấn đề
Phân Biệt Sử Dụng Giữa ArrayList Và LinkedList
Việc chọn triển khai List nào để sử dụng là quan trọng và nên dựa trên nội dung xử lý và lượng dữ liệu. Các tiêu chí sau có thể làm hướng dẫn:
- ArrayList : Truy cập ngẫu nhiên thường xuyên, chủ yếu đọc
- LinkedList : Chèn/xóa thường xuyên, thứ tự truy cập quan trọng
Hướng Tới Học Tập Tương Lai
List chỉ là “điểm khởi đầu” cho các bộ sưu tập của Java. Để xử lý các cấu trúc dữ liệu và tiện ích nâng cao hơn, khuyến nghị sâu sắc hóa hiểu biết về các lớp và tính năng sau:
- Set và Map : Quản lý các phần tử duy nhất, cấu trúc cặp khóa-giá trị
- Lớp tiện ích Collections : Sắp xếp, tìm min/max, v.v.
- Sử dụng Stream API : Giới thiệu lập trình chức năng
- Hiểu Generics : Các hoạt động bộ sưu tập an toàn kiểu
Làm chủ các kiến thức cơ bản của List sẽ làm cho lập trình Java tổng thể của bạn dễ quản lý hơn đáng kể.
Câu Hỏi Thường Gặp (FAQ)
Chúng tôi đã tổng hợp các điểm mà người mới bắt đầu thường có câu hỏi về List của Java. Chúng tôi đã chọn nội dung thường gặp trong thực tế.
.### Q1. Sự khác nhau giữa List và Array trong Java là gì?
A. Mảng có số phần tử cố định và kích thước của nó phải được xác định khi khai báo. Ngược lại, List có kích thước biến đổi, cho phép thêm và xóa phần tử một cách linh hoạt. Hơn nữa, List cung cấp nhiều phương thức tiện lợi (add, remove, contains, v.v.) và vượt trội về khả năng đọc và bảo trì.
Q2. Nên dùng ArrayList hay LinkedList?
A. ArrayList thích hợp chủ yếu khi có việc truy cập ngẫu nhiên (lấy phần tử theo chỉ mục) thường xuyên. LinkedList thích hợp khi việc chèn và xóa phần tử diễn ra thường xuyên. Nếu không chắc, bắt đầu với ArrayList thường được khuyến nghị.
Q3. Tôi có thể lưu các kiểu nguyên thủy (như int hoặc double) trong List không?
A. Không trực tiếp. Vì List trong Java chỉ xử lý các kiểu đối tượng, nên với các kiểu nguyên thủy như int, bạn cần sử dụng các lớp wrapper tương ứng (Integer, Double, v.v.).
List<Integer> numbers = new ArrayList<>();
numbers.add(10); // Auto-boxed and stored as Integer type
Q4. Làm sao để sắp xếp các phần tử trong List?
A. Bạn có thể sắp xếp theo thứ tự tăng dần bằng Collections.sort(list). Ngoài ra, nếu muốn sắp xếp theo thứ tự tùy chỉnh, bạn có thể chỉ định một Comparator để sắp xếp linh hoạt.
Q5. Tôi nên làm gì nếu muốn quản lý các phần tử mà không có trùng lặp?
A. List là một collection cho phép trùng lặp. Nếu muốn tránh trùng lặp, hãy cân nhắc sử dụng Set (ví dụ, HashSet). Tuy nhiên, lưu ý rằng thứ tự không được đảm bảo. Nếu muốn loại bỏ trùng lặp trong khi vẫn giữ List, có thể sử dụng xử lý Stream sau:
List<String> distinctList = list.stream()
.distinct()
.collect(Collectors.toList());
Q6. Làm sao để xóa tất cả các phần tử khỏi một List?
A. Bạn có thể xóa tất cả các phần tử trong List bằng phương thức clear().
list.clear();
Q7. Các thao tác được sử dụng thường xuyên nhất trong List là gì?
A. Các thao tác được sử dụng thường xuyên nhất trong thực tế là add (thêm), get (lấy), remove (xóa), và size (lấy kích thước). Nắm vững những thao tác này sẽ bao phủ hầu hết các xử lý cơ bản.

