1. Tìm kiếm phần tử trong các bộ sưu tập
Tìm kiếm bằng các phương thức chuẩn
Khi chúng ta có một danh sách, ví dụ, các tên:
List<String> names = List.of("Anna", "Boris", "Viktoriya", "Anna", "Dmitriy");
Java cung cấp một số phương thức tiện dụng để tìm kiếm:
- contains(Object o) — kiểm tra xem phần tử có trong bộ sưu tập hay không.
- indexOf(Object o) — trả về chỉ số đầu tiên của phần tử (hoặc -1 nếu không tìm thấy).
- lastIndexOf(Object o) — trả về chỉ số cuối cùng của phần tử (hoặc -1).
Ví dụ:
System.out.println(names.contains("Anna")); // true
System.out.println(names.indexOf("Anna")); // 0
System.out.println(names.lastIndexOf("Anna")); // 3
System.out.println(names.contains("Sergey")); // false
Thực tế: Đối với các bộ sưu tập kiểu Set, không có các phương thức indexOf và lastIndexOf — trong Set không có chỉ số, chỉ có các giá trị duy nhất.
Tìm kiếm theo điều kiện
Đôi khi cần tìm không phải một giá trị cụ thể mà là phần tử thỏa điều kiện. Ví dụ, tìm tên đầu tiên dài hơn 6 ký tự. Vòng lặp for thông thường rất phù hợp: ngay khi tìm được phần tử phù hợp — hãy dùng break.
List<String> names = List.of("Anna", "Boris", "Viktoriya", "Anna", "Dmitriy");
String found = null;
for (String name : names) {
if (name.length() > 6) {
found = name;
break; // Đã tìm thấy phần tử đầu tiên phù hợp, có thể thoát khỏi vòng lặp
}
}
System.out.println(found); // Viktoriya
Tìm giá trị lớn nhất và nhỏ nhất
Với các bộ sưu tập có phần tử có thể so sánh được (ví dụ: số), có thể nhanh chóng tìm max và min bằng lớp Collections.
List<Integer> numbers = List.of(10, 5, 20, 7, 20, 3);
int max = Collections.max(numbers); // 20
int min = Collections.min(numbers); // 3
System.out.println("Tối đa: " + max);
System.out.println("Tối thiểu: " + min);
Quan trọng: Các phần tử phải có khả năng so sánh (triển khai Comparable), nếu không bạn cần truyền vào một comparator.
2. Sắp xếp các bộ sưu tập
Sắp xếp danh sách theo mặc định
Chỉ có thể sắp xếp các danh sách có thể thay đổi (ví dụ: ArrayList, LinkedList). Các bộ sưu tập kiểu Set và Map không hỗ trợ sắp xếp trực tiếp.
Mặc định, sắp xếp theo “thứ tự tự nhiên”: với số — tăng dần, với chuỗi — theo bảng chữ cái.
List<Integer> numbers = new ArrayList<>(List.of(5, 1, 7, 3));
Collections.sort(numbers);
System.out.println(numbers); // [1, 3, 5, 7]
Có thể làm điều tương tự thông qua phương thức danh sách sort:
List<Integer> numbers = new ArrayList<>(List.of(5, 1, 7, 3));
numbers.sort(null); // null nghĩa là: dùng thứ tự tự nhiên
System.out.println(numbers); // [1, 3, 5, 7]
Sắp xếp với comparator
Nếu cần quy tắc riêng — ví dụ, sắp xếp chuỗi theo độ dài:
List<String> words = new ArrayList<>(List.of("kot", "slon", "mukha", "nosorog"));
words.sort(Comparator.comparingInt(String::length));
System.out.println(words); // [kot, mukha, slon, nosorog]
Theo thứ tự ngược lại:
words.sort(Comparator.comparingInt(String::length).reversed());
System.out.println(words); // [nosorog, mukha, slon, kot]
Phong cách cũ thông qua Collections.sort:
Collections.sort(words, Comparator.comparingInt(String::length));
3. Đặc điểm sắp xếp các bộ sưu tập khác nhau
Sắp xếp chỉ thay đổi danh sách
Các phương thức sắp xếp (sort, Collections.sort) chỉ hoạt động với các bộ sưu tập có thứ tự phần tử (ví dụ, List). Với Set và Map thì không có các phương thức như vậy.
Thử sắp xếp Set:
Set<Integer> mySet = new HashSet<>(List.of(3, 1, 2));
Collections.sort(mySet); // Lỗi biên dịch: sort yêu cầu List!
Nếu cần lấy một danh sách đã sắp xếp từ Set:
List<Integer> sortedFromSet = new ArrayList<>(mySet);
Collections.sort(sortedFromSet);
System.out.println(sortedFromSet); // [1, 2, 3]
Sắp xếp Map: theo khóa và theo giá trị
Không thể sắp xếp Map trực tiếp, nhưng có thể:
- Lấy danh sách các cặp “key-value”
- Sắp xếp danh sách đó theo cách mong muốn
- Nếu cần, gom lại thành một Map mới
Ví dụ: sắp xếp theo giá trị
Map<String, Integer> scores = Map.of(
"Anna", 15,
"Boris", 20,
"Vika", 12
);
List<Map.Entry<String, Integer>> entries = new ArrayList<>(scores.entrySet());
entries.sort(Map.Entry.comparingByValue());
for (Map.Entry<String, Integer> entry : entries) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Vika: 12
// Anna: 15
// Boris: 20
Sắp xếp đối tượng theo trường
Giả sử chúng ta có một lớp người dùng:
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
Và một danh sách người dùng:
List<User> users = new ArrayList<>(List.of(
new User("Anna", 22),
new User("Boris", 18),
new User("Vika", 25)
));
Sắp xếp theo tuổi:
users.sort(Comparator.comparingInt(u -> u.age));
System.out.println(users); // [Boris (18), Anna (22), Vika (25)]
Theo thứ tự ngược lại:
users.sort(Comparator.comparingInt((User u) -> u.age).reversed());
System.out.println(users); // [Vika (25), Anna (22), Boris (18)]
4. Các lỗi thường gặp khi tìm kiếm và sắp xếp bộ sưu tập
Lỗi #1: Sắp xếp danh sách bất biến. Các danh sách được tạo qua List.of(...) là không thể thay đổi — khi gọi sort sẽ nhận UnsupportedOperationException.
List<Integer> immutable = List.of(3, 1, 2);
immutable.sort(null); // Ném ngoại lệ!
Lỗi #2: Sắp xếp trực tiếp Set hoặc Map. Các phương thức sắp xếp không hoạt động với Set và Map. Nếu cần sắp xếp, hãy chuyển dữ liệu thành danh sách trước.
Lỗi #3: So sánh đối tượng không đúng cách. Đừng trừ số nguyên trực tiếp khi so sánh — có thể gây tràn số. Thay vì u1.age - u2.age hãy dùng Integer.compare hoặc Comparator.comparingInt.
// users.sort((u1, u2) -> u1.age - u2.age); // Chạy được, nhưng nguy hiểm do tràn số!
Lỗi #4: Dùng contains cho tìm kiếm phức tạp. Phương thức contains chỉ kiểm tra trùng khớp hoàn toàn (theo equals). Nếu cần tìm theo một phần chuỗi hoặc theo trường của đối tượng — hãy dùng vòng lặp với điều kiện hoặc comparator/stream.
GO TO FULL VERSION