Khi làm việc với Java, một trong những giao diện (interface) quan trọng và phổ biến nhất mà bạn sẽ gặp phải là List interface Hãy cùng Rikkei Academy tìm hiểu chi tiết về List trong Java, các các phương thức và các lớp thực thi (implementation) phổ biến của nó.
List Trong Java là gì?
List trong Java là một interface được sử dụng để đại diện cho một danh sách hay bộ sưu tập các phần tử theo thứ tự. Nó là một trong những interface được sử dụng phổ biến nhất trong Java Collections Framework.
Một số đặc điểm của List trong java gồm:
- List cho phép các phần tử được thêm vào và truy xuất theo vị trí của chúng trong danh sách do nó giữ lại thứ tự chèn.
- List cho phép chứa các phần tử có giá trị trùng lặp và hỗ trợ các phương thức để thêm, xóa, truy xuất và sắp xếp các phần tử trong danh sách.
- List kế thừa từ Collections Interface và bạn có thể sử dụng List bằng cách cài đặt gói java.util.
- List trong Java là nơi tạo ra các đối tượng ListIterator. ListIterator được dùng để lặp lại danh sách theo hướng thuận hoặc ngược và thực hiện các thao tác khác trên các phần tử trong danh sách.
Các lớp trong Java List
- Cấu trúc các lớp List trong Java
Các lớp AbstractList, CopyOnWriteArrayList và AbstractSequentialList là các lớp trừu tượng giao diện List. Mỗi lớp có một chức năng riêng biệt, được mô tả như sau:
- AbstractList: Cung cấp một số phương thức chung cho tất cả các lớp triển khai của giao diện List. Nó không cung cấp một triển khai cụ thể cho giao diện List, mà yêu cầu các lớp con triển khai các phương thức trừu tượng.
- CopyOnWriteArrayList: Triển khai một danh sách có thể được truy cập đồng thời bởi nhiều luồng mà không cần đồng bộ hóa.Trong đó tất cả các thay đổi được thực hiện bằng cách tạo một bản sao mới của danh sách.
- AbstractSequentialList: Thực hiện giao diện Collection và lớp AbstractCollection. Nó được sử dụng để triển khai các danh sách tuần tự, nơi mà phần tử tiếp theo của danh sách phụ thuộc vào phần tử trước đó của danh sách.
Tiếp theo chúng ta sẽ thảo luận về các lớp triển khai cụ thể, và cách tạo đối tượng danh sách sử dụng từng lớp:
ArrayList
Lớp ArrayList cung cấp cho chúng ta mảng động trong Java. Mặc dù nó có thể chậm hơn so với các mảng tiêu chuẩn nhưng có thể hữu ích trong các chương trình yêu cầu nhiều thao tác trên mảng.
Ví dụ:
import java.util.ArrayList;
public class ArrayListExample { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); //tạo đối tượng danh sách list.add(“Java”); //thêm phần tử vào danh sách list.add(“Python”); list.add(“C++”); System.out.println(“Danh sách các ngôn ngữ lập trình: ” + list); System.out.println(“Kích thước của danh sách: ” + list.size()); } } |
LinkedList
Lớp LinkedList được thực hiện bằng cách sử dụng cấu trúc dữ liệu danh sách liên kết. Các phần tử trong danh sách không được lưu trữ ở các vị trí liên tiếp, mỗi phần tử là một đối tượng riêng biệt với phần dữ liệu và phần địa chỉ. Các phần tử được liên kết với nhau bằng các con trỏ và địa chỉ. Do tính động và dễ chèn, xóa, LinkedList thường được ưu tiên hơn so với các mảng (array). Ví dụ:
import java.util.LinkedList;
public class LinkedListExample { public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<String>(); //tạo đối tượng linked list linkedList.add(“Java”); //thêm phần tử vào linked list linkedList.add(“Python”); linkedList.add(“C++”); System.out.println(“Các ngôn ngữ lập trình trong linked list: ” + linkedList); System.out.println(“Kích thước của linked list: ” + linkedList.size()); } } |
Vector
Lớp Vector thực hiện một mảng có thể mở rộng của các đối tượng. Vector cũng có thể được coi như một phiên bản cũ của ArrayList.
Ví dụ:
import java.util.Vector;
public class VectorExample { public static void main(String[] args) { Vector<String> vector = new Vector<String>(); //tạo đối tượng vector vector.add(“Java”); //thêm phần tử vào vector vector.add(“Python”); vector.add(“C++”); System.out.println(“Các ngôn ngữ lập trình trong vector: ” + vector); System.out.println(“Kích thước của vector: ” + vector.size()); } } |
Stack
Lớp Stack thực hiện cấu trúc dữ liệu Stack (ngăn xếp), được xây dựng trên nguyên tắc last-in-first-out. Bạn có thể tưởng tượng stack tương tự giống trò chơi xếp gỗ vậy. Mỗi khi bạn đặt một khối hình mới lên trên xếp hình, nó sẽ được đặt lên đỉnh của xếp hình, và khi bạn muốn lấy khối hình ra, bạn sẽ lấy từ đỉnh xếp hình.
Ngoài các hoạt động đẩy và lấy cơ bản, lớp stack còn cung cấp ba chức năng khác là empty, search và peek.
Ví dụ:
import java.util.Stack;
public class StackExample { public static void main(String[] args) { Stack<String> stack = new Stack<String>(); //tạo đối tượng stack stack.push(“Java”); //đẩy phần tử vào stack stack.push(“Python”); stack.push(“C++”); System.out.println(“Các ngôn ngữ lập trình trên stack: ” + stack); System.out.println(“Phần tử đầu tiên của stack: ” + stack.peek()); System.out.println(“Kích thước của stack: ” + stack.size()); } } |
Khai báo List trong Java
Giao diện List trong Java được khai báo như sau:
public interface List<E> extends Collection<E>; |
Vì List là một giao diện, các đối tượng không thể được tạo ra dưới định dạng list. Nói cách khác, để tạo ra một đối tượng của List, chúng ta cần sử dụng một lớp triển khai giao diện List, ví dụ như ArrayList hoặc LinkedList. Tại phiên bản 5.0, Java đã tích hợp kiểu Generics, từ đây ta có thể giới hạn loại đối tượng được lưu trữ trong List, nghĩa là chúng ta có thể khai báo kiểu dữ liệu của các phần tử trong List, từ đó giúp tăng tính an toàn và độ tin cậy của mã.
Ví dụ, nếu chúng ta muốn tạo một List chỉ chứa các đối tượng của kiểu Integer, ta có thể khai báo như sau:
List<Integer> myList = new ArrayList<>(); |
Ở đây, lớp ArrayList là một trong các lớp triển khai của giao diện List và được xác định trong gói java.util.
Cú pháp của List trong Java
Cú pháp của List trong Java như sau:
List<DataType> listName = new ArrayList<>(); |
Trong đó:
- DataType là kiểu dữ liệu của các phần tử trong danh sách.
- listName là tên của đối tượng danh sách.
- ArrayList() là một trong các lớp triển khai của giao diện List, được sử dụng để tạo ra một đối tượng ArrayList mới.
Một số tài liệu có thể dùng Obj thay thế cho DataType. Xét về mặt kỹ thuật, chúng tương tự nhau, cả hai đều được sử dụng để đại diện cho kiểu dữ liệu cụ thể của đối tượng được lưu trữ trong danh sách. Tuy nhiên, khi sử dụng Generics trong Java, việc sử dụng tên biến có ý nghĩa rõ ràng và dễ hiểu sẽ làm cho mã nguồn trở nên dễ đọc và dễ bảo trì hơn.
Ngoài ArrayList chúng ta còn có thể sử dụng các lớp khác như LinkedList, Stack…để triển khai List. Ví dụ:
List<String> names = new LinkedList<>(); |
List<Object> objects = new Vector<>(); |
Trong đó, names là một danh sách các chuỗi và objects là một danh sách các đối tượng.
Phương thức trong List Java
Dưới đây là bảng tổng hợp các phương thức của List Interface trong Java, lưu ý, nó chưa bao gồm các phương thức kế thừa từ các Interface khác như Collection, Iterable và Object.
Phương thức | Mô tả |
add(E element) | Thêm một phần tử vào cuối danh sách. |
add(int index, E element) | Thêm một phần tử vào vị trí chỉ định trong danh sách. |
addAll(Collection<? extends E> c) | Thêm tất cả các phần tử trong Collection chỉ định vào cuối danh sách. |
addAll(int index, Collection<? extends E> c) | Thêm tất cả các phần tử trong Collection chỉ định vào vị trí chỉ định trong danh sách. |
clear() | Xóa tất cả các phần tử trong danh sách. |
contains(Object o) | Kiểm tra xem danh sách có chứa đối tượng định trước hay không. |
containsAll(Collection<?> c) | Kiểm tra xem danh sách có chứa tất cả các phần tử trong Collection chỉ định hay không. |
equals(Object o) | So sánh danh sách với đối tượng chỉ định để xác định liệu chúng có bằng nhau hay không. |
get(int index) | Trả về phần tử tại vị trí chỉ định trong danh sách. |
hashCode() | Trả về mã băm của danh sách. |
indexOf(Object o) | Trả về vị trí đầu tiên của phần tử trong danh sách có giá trị bằng với đối tượng chỉ định. |
isEmpty() | Kiểm tra xem danh sách có rỗng hay không. |
iterator() | Trả về một Iterator để lặp lại các phần tử trong danh sách. |
lastIndexOf(Object o) | Trả về vị trí cuối cùng của phần tử trong danh sách có giá trị bằng với đối tượng chỉ định. |
listIterator() | Trả về một ListIterator để lặp lại các phần tử trong danh sách và cho phép thay đổi danh sách. |
listIterator(int index) | Trả về một ListIterator để lặp lại các phần tử trong danh sách từ vị trí chỉ định và cho phép thay đổi danh sách. |
remove(int index) | Xóa phần tử tại vị trí chỉ định trong danh sách. |
remove(Object o) | Xóa phần tử đầu tiên trong danh sách có giá trị bằng với đối tượng chỉ định. |
removeAll(Collection<?> c) | Xóa tất cả các phần tử trong danh sách có trong Collection chỉ định. |
replaceAll(UnaryOperator<E> operator) | Thay thế tất cả các phần tử trong danh sách bằng kết quả của áp dụng toán tử đơn nguyên chỉ định tới mỗi phần tử. |
retainAll(Collection<?> c) | Xóa tất cả các phần tử trong danh sách không có trong Collection chỉ định. |
set(int index, E element) | Thay thế phần tử tại vị trí chỉ định trong danh sách bằng một phần tử mới. |
size() | Trả về số lượng phần tử trong danh sách. |
sort(Comparator<? super E> c) | Sắp xếp lại các phần tử trong danh sách bằng cách sử dụng Comparator chỉ định. |
subList(int fromIndex, int toIndex) | Trả về một danh sách con của danh sách, chứa các phần tử từ vị trí fromIndex đến vị trí toIndex – 1. |
toArray() | Chuyển đổi danh sách thành một mảng. |
toArray(T[] a) | Chuyển đổi danh sách thành một mảng, với kiểu phần tử chỉ định. |
Lưu ý: List là một interface trong Java. Do đó, nó không thể tự triển khai các phương thức. Thay vào đó, để sử dụng các phương thức của List, chúng ta cần sử dụng các lớp triển khai cụ thể của List.
Các thao tác trong Java List
Dưới đây là ví dụ về một số thao tác phổ biến sử dụng các phương thức có trong List Java thông qua lớp triển khai ArrayList:
Thêm phần tử
Để thêm một phần tử vào danh sách, chúng ta có thể sử dụng phương thức add(). Phương thức này được nạp chồng để thực hiện nhiều thao tác khác nhau dựa trên các tham số khác nhau.
- add(Object): Phương thức này được sử dụng để thêm một phần tử vào cuối danh sách.
- add(int index, Object): Phương thức này được sử dụng để thêm một phần tử vào một vị trí cụ thể trong danh sách.
import java.util.ArrayList;
public class ArrayListExample { public static void main(String[] args) { ArrayList<String> ten = new ArrayList<String>(); // thêm phần tử vào danh sách ten.add(“Alice”); ten.add(“Bob”); ten.add(“Charlie”); // in ra danh sách ban đầu System.out.println(“Danh sách ban đầu: ” + ten); // thêm một phần tử vào danh sách ở vị trí cụ thể ten.add(1, “David”); // in ra danh sách đã được cập nhật System.out.println(“Danh sách sau khi thêm ‘David’ vào vị trí 1: ” + ten); } |
Cập nhật phần tử
Sau khi thêm các phần tử vào danh sách, nếu chúng ta muốn thay đổi phần tử, có thể sử dụng phương thức set(). Vì List được đánh chỉ mục, phần tử mà chúng ta muốn thay đổi được tham chiếu bằng chỉ mục của phần tử đó. Do đó, phương thức này nhận một chỉ mục và phần tử được cập nhật cần được chèn vào chỉ mục đó.
import java.util.ArrayList;
public class ArrayListExample { public static void main(String[] args) { ArrayList<String> ten = new ArrayList<String>(); // thêm phần tử vào danh sách ten.add(“Alice”); ten.add(“Bob”); ten.add(“Charlie”); // in ra danh sách ban đầu System.out.println(“Danh sách ban đầu: ” + ten); // cập nhật phần tử ở vị trí cụ thể trong danh sách ten.set(1, “David”); // in ra danh sách đã được cập nhật System.out.println(“Danh sách sau khi cập nhật phần tử ở vị trí 1: ” + ten); } } |
Tìm kiếm phần tử
Giao diện List cung cấp nhiều phương thức để tìm kiếm phần tử, chẳng hạn như phương thức indexOf() và lastIndexOf(). Trong đó;
- indexOf(element): Trả về chỉ mục của lần xuất hiện đầu tiên của phần tử cụ thể trong danh sách hoặc -1 nếu phần tử không được tìm thấy.
- lastIndexOf(element): Trả về chỉ mục của lần xuất hiện cuối cùng của phần tử cụ thể trong danh sách hoặc -1 nếu phần tử không được tìm thấy.
Ví dụ:
import java.util.ArrayList;
public class ArrayListExample { public static void main(String[] args) { ArrayList<String> ten = new ArrayList<String>(); // thêm phần tử vào danh sách ten.add(“Alice”); ten.add(“Bob”); ten.add(“Charlie”); // tìm kiếm phần tử trong danh sách if (ten.contains(“Bob”)) { System.out.println(“Phần tử ‘Bob’ được tìm thấy trong danh sách.”); } else { System.out.println(“Phần tử ‘Bob’ không được tìm thấy trong danh sách.”); } // tìm kiếm phần tử trong danh sách và trả về vị trí của phần tử đó int index = ten.indexOf(“Charlie”); if (index != -1) { System.out.println(“Phần tử ‘Charlie’ được tìm thấy ở vị trí ” + index + ” trong danh sách.”); } else { System.out.println(“Phần tử ‘Charlie’ không được tìm thấy trong danh sách.”); } } } |
Xóa phần tử
Để xóa một phần tử khỏi danh sách, chúng ta có thể sử dụng phương thức remove(). Phương thức này được nạp chồng để thực hiện nhiều thao tác khác nhau dựa trên các tham số khác nhau.
- remove(Object): Phương thức này được sử dụng để đơn giản xóa một đối tượng khỏi List. Nếu có nhiều đối tượng như vậy, thì đối tượng xuất hiện đầu tiên sẽ bị xóa.
- remove(int index): Vì một List được chỉ mục, phương thức này nhận một giá trị số nguyên để đơn giản xóa phần tử hiện có tại chỉ mục cụ thể đó trong danh sách. Sau khi xóa phần tử, tất cả các phần tử được di chuyển sang trái để điền vào khoảng trống và các chỉ mục của đối tượng được cập nhật.
import java.util.ArrayList;
public class ArrayListExample { public static void main(String[] args) { ArrayList<String> ten = new ArrayList<String>(); // thêm phần tử vào danh sách ten.add(“Alice”); ten.add(“Bob”); ten.add(“Charlie”); // in ra danh sách ban đầu System.out.println(“Danh sách ban đầu: ” + ten); // xóa phần tử khỏi danh sách bằng tên phần tử ten.remove(“Bob”); // in ra danh sách sau khi xóa phần tử System.out.println(“Danh sách sau khi xóa phần tử ‘Bob’: ” + ten); // xóa phần tử khỏi danh sách bằng vị trí ten.remove(1); // in ra danh sách sau khi xóa phần tử System.out.println(“Danh sách sau khi xóa phần tử ở vị trí 1: ” + ten); } } |
Truy cập phần tử
Để truy cập một phần tử trong danh sách, chúng ta có thể sử dụng phương thức get(), trả về phần tử ở chỉ mục được chỉ định.
- get(int index): Phương thức này trả về phần tử tại chỉ mục được chỉ định trong danh sách.
import java.util.ArrayList;
public class ArrayListExample { public static void main(String[] args) { ArrayList<String> ten = new ArrayList<String>(); // thêm phần tử vào danh sách ten.add(“Alice”); ten.add(“Bob”); ten.add(“Charlie”); // truy cập phần tử trong danh sách String phanTu = ten.get(1); System.out.println(“Phần tử ở vị trí 1 trong danh sách là: ” + phanTu); } } |
Kiểm tra phần tử
Để kiểm tra xem một phần tử có tồn tại trong danh sách hay không, chúng ta có thể sử dụng phương thức contains(). Phương thức này trả về true nếu phần tử được chỉ định có trong danh sách, ngược lại, nó trả về false.
- contains(Object): Phương thức này nhận một tham số đơn, đối tượng cần kiểm tra xem có trong danh sách hay không.
import java.util.ArrayList;
public class ArrayListExample { public static void main(String[] args) { ArrayList<String> ten = new ArrayList<String>(); // thêm phần tử vào danh sách ten.add(“Alice”); ten.add(“Bob”); ten.add(“Charlie”); // kiểm tra phần tử trong danh sách boolean coBob = ten.contains(“Bob”); boolean coDave = ten.contains(“Dave”); // in ra kết quả kiểm tra System.out.println(“Có phần tử ‘Bob’ trong danh sách: ” + coBob); System.out.println(“Có phần tử ‘Dave’ trong danh sách: ” + coDave); } } |
Phép lặp (Iteration) trong Java List
Trong các ví dụ, hầu hết chúng ta đều làm việc với các dữ liệu có kích thước nhỏ và các thao tác đều làm thủ công. Tuy nhiên, nếu chúng ta cần thực hiện thao tác như xử lý hay sửa đổi trên một nhóm dữ liệu có kích thước lớn hơn thì phải làm như thế nào?
Có nhiều cách để lặp lại danh sách. Các cách phổ biến nhất là sử dụng vòng lặp for cơ bản kết hợp với phương thức get() để lấy phần tử tại một chỉ mục cụ thể hoặc vòng lặp for cải tiến: for-each. Ví dụ:
import java.util.List;
import java.util.ArrayList; import java.util.Arrays; public class ListIteration { public static void main(String[] args) { // Create a List of strings List<String> list = new ArrayList<>(Arrays.asList(“a”, “b”, “c”)); // Using for loop for (int i = 0; i < list.size(); i++) { String element = list.get(i); System.out.println(i + “: ” + element); } // Using for-each loop for (String element : list) { System.out.println(element); } } } |
So sánh List và Set
Cả List và Set đều kế thừa Collection interface và có những điểm tương đồng nhất định. Tuy nhiên, để có thể lựa chọn Interface phù hợp cho nhu cầu sử dụng cũng như tránh các rủi ro như hiệu suất kém hay lỗi trong quá trình thực thi thì việc hiểu rõ sự khác biệt giữa List và Set là rất cần thiết:
List | Set |
Là một chuỗi có thứ tự | Là một chuỗi không có thứ tự |
Cho phép các phần tử trùng lặp | Không cho phép các phần tử trùng lặp |
Có thể truy cập phần tử dựa trên vị trí của nó, bằng cách dùng chỉ số | Không thể truy cập phần tử dựa trên vị trí của nó |
Có thể lặp lại các phần tử theo thứ tự | Không đảm bảo thứ tự khi lặp lại các phần tử |
Có thể chứa nhiều phần tử null | Mỗi phần tử null chỉ có thể xuất hiện một lần |
Các lớp triển khai ArrayList, LinkedList, Vector, Stack | Các lớp triển khai HashSet, LinkedHashSet, TreeSet |
Kết luận
Như vậy, chúng ta đã tìm hiểu về List trong Java, một trong những giao diện quan trọng nhất trong Java. Chúng ta đã tìm hiểu về các phương thức cơ bản, các thao tác và các lớp triển khai phổ biến. Hy vọng bài viết này đã giúp ích cho bạn trong việc tìm hiểu kiến thức và thực hành hiệu quả với ngôn ngữ lập trình Java.
Nếu bạn đang muốn tìm địa chỉ học uy tín, hãy tham khảo khóa học lập trình tại Rikkei Academy! Với lộ trình tinh gọn, bám sát thực tế, giảng viên hỗ trợ 24/7 giúp bạn nắm bắt sự nghiệp lập trình trong 6 tháng! Để tìm hiểu thêm thông tin chi tiết, đăng ký nhận tư vấn miễn phí ngay tại đây!