110 câu hỏi về Collection Framework nâng cao

Phần 1: Kiến thức nội bộ và nâng cao Collection Framework (20 câu)


1. HashMap hoạt động nội bộ như thế nào?

Trả lời: HashMap sử dụng bảng băm (hash table). Các key được chuyển đổi thành giá trị hashCode(), từ đó xác định vị trí lưu trữ trong bucket. Nếu nhiều key có cùng hashCode, chúng sẽ được lưu trong một danh sách liên kết hoặc cây nhị phân (từ Java 8).


2. ConcurrentHashMap khác gì so với HashMap về mặt hoạt động?

Trả lời: ConcurrentHashMap chia dữ liệu thành các phân đoạn (segments) để giảm xung đột khi nhiều luồng truy cập. HashMap không hỗ trợ thread-safe.


3. Khi nào HashMap chuyển từ danh sách liên kết sang cây nhị phân?

Trả lời: Từ Java 8, nếu số phần tử trong một bucket vượt quá 8 và kích thước HashMap lớn hơn 64, danh sách liên kết sẽ chuyển thành cây nhị phân để tăng hiệu suất tìm kiếm.


4. WeakHashMap xử lý các khóa như thế nào?

Trả lời: WeakHashMap sử dụng khóa là weak references. Nếu không có tham chiếu mạnh đến một khóa, nó sẽ bị garbage collector thu hồi.


5. Tại sao TreeMap không cho phép khóa null?

Trả lời: Vì TreeMap dựa trên việc so sánh các khóa để duy trì thứ tự, và việc so sánh null sẽ gây lỗi.


6. NavigableSet cung cấp các phương thức điều hướng nào?

Trả lời: Các phương thức như floor(), ceiling(), lower(), higher(), cho phép truy vấn phần tử gần nhất lớn hơn hoặc nhỏ hơn một giá trị cụ thể.


7. Cơ chế của fail-fast Iterator trong Java là gì?

Trả lời: Fail-fast Iterator ném ConcurrentModificationException nếu phát hiện Collection bị thay đổi cấu trúc trong khi duyệt.


8. Làm thế nào để tránh fail-fast trong Collection?

Trả lời: Sử dụng các Collection thread-safe như ConcurrentHashMap, CopyOnWriteArrayList, hoặc duyệt qua bản sao của Collection.


9. IdentityHashMap sử dụng cơ chế gì để so sánh khóa?

Trả lời: IdentityHashMap sử dụng so sánh bằng == (so sánh tham chiếu) thay vì equals().


10. Tại sao CopyOnWriteArrayList phù hợp với thao tác đọc nhiều, ghi ít?

Trả lời: Vì nó tạo bản sao mới khi ghi, đảm bảo thread-safe nhưng không ảnh hưởng đến các luồng đang đọc.


11. Sự khác biệt giữa LinkedHashMap và TreeMap về cách duy trì thứ tự?

Trả lời:

  • LinkedHashMap: Duy trì thứ tự chèn.

  • TreeMap: Sắp xếp các phần tử theo thứ tự tự nhiên hoặc Comparator tùy chỉnh.


12. Cách hoạt động của computeIfAbsent() trong Map?

Trả lời: Tính toán giá trị mới và thêm vào Map nếu khóa không tồn tại.

map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);

13. Tại sao cần cấu hình initialCapacityloadFactor trong HashMap?

Trả lời: Để giảm số lần rehash, tăng hiệu suất khi làm việc với lượng dữ liệu lớn.


14. Cách hoạt động của Collections.synchronizedList()?

Trả lời: Tạo một lớp bao bọc thread-safe cho List bằng cách đồng bộ hóa trên các phương thức của nó.


15. Tại sao không nên sử dụng Vector trong các ứng dụng hiện đại?

Trả lời: Vector là thread-safe nhưng không hiệu quả, do mọi phương thức đều được đồng bộ hóa, gây ra tắc nghẽn khi sử dụng trong đơn luồng.


16. Điều gì xảy ra nếu thêm một phần tử null vào TreeSet?

Trả lời: Sẽ ném ngoại lệ NullPointerException vì TreeSet cần so sánh các phần tử để sắp xếp.


17. ConcurrentSkipListMap hoạt động như thế nào?

Trả lời: Dựa trên Skip List, một cấu trúc dữ liệu phân tầng cho phép tìm kiếm và sửa đổi nhanh. Nó thread-safe và duy trì thứ tự tự nhiên hoặc Comparator tùy chỉnh.


18. Làm thế nào để kiểm tra tính thread-safe của một Collection?

Trả lời: Sử dụng lớp Collections như Collections.synchronizedList() hoặc chuyển sang các lớp thread-safe như ConcurrentHashMap.


19. Tại sao nên sử dụng EnumSet thay vì HashSet khi làm việc với Enum?

Trả lời: EnumSet được tối ưu hóa để lưu trữ các giá trị Enum, sử dụng bit vector thay vì bảng băm, giúp tiết kiệm bộ nhớ và tăng hiệu suất.


20. Điều gì làm cho fail-safe Iterator an toàn hơn fail-fast Iterator?

Trả lời: Fail-safe Iterator hoạt động trên bản sao của Collection, vì vậy nó không bị ảnh hưởng bởi các thay đổi đồng thời trong Collection gốc.


Phần 2: Tình huống thực tế và ứng dụng Collection (30 câu)

21. Làm thế nào để quản lý hàng đợi công việc với PriorityQueue?

Trả lời:
Sử dụng PriorityQueue để ưu tiên các công việc dựa trên độ ưu tiên:

PriorityQueue<Job> queue = new PriorityQueue<>(Comparator.comparingInt(Job::getPriority));
queue.add(new Job("Task 1", 1)); // Ưu tiên cao hơn
queue.add(new Job("Task 2", 2));  
Job nextJob = queue.poll(); // Lấy công việc có độ ưu tiên cao nhất

22. Làm thế nào để sử dụng WeakHashMap để tạo bộ nhớ tạm?

Trả lời: WeakHashMap tự động xóa các mục không còn tham chiếu mạnh:

WeakHashMap<String, String> cache = new WeakHashMap<>();
cache.put(new String("key1"), "value1");
System.gc(); // Thu hồi key không được tham chiếu
System.out.println(cache.size()); // Có thể trả về 0

23. Làm thế nào để tìm tất cả phần tử trùng lặp trong một danh sách?

Trả lời:

Set<Integer> unique = new HashSet<>();
Set<Integer> duplicates = list.stream()
                               .filter(e -> !unique.add(e))
                               .collect(Collectors.toSet());

24. Làm thế nào để sắp xếp một Map theo giá trị?

Trả lời:

Map<String, Integer> sorted = map.entrySet()
                                 .stream()
                                 .sorted(Map.Entry.comparingByValue())
                                 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                                                           (e1, e2) -> e1, LinkedHashMap::new));

25. Sử dụng TreeMap để thực hiện xếp hạng sản phẩm theo điểm đánh giá.

Trả lời:

TreeMap<Integer, String> rankings = new TreeMap<>(Comparator.reverseOrder());
rankings.put(5, "Product A");
rankings.put(3, "Product B");
System.out.println(rankings); // Sản phẩm được xếp từ cao đến thấp

26. Làm thế nào để duyệt qua các phần tử trong Map mà không sử dụng vòng lặp?

Trả lời:
Sử dụng Stream API:

map.forEach((key, value) -> System.out.println(key + ": " + value));

27. Làm thế nào để loại bỏ tất cả các phần tử null khỏi một List?

Trả lời:

list.removeIf(Objects::isNull);

28. Làm thế nào để tìm các phần tử chung giữa hai danh sách?

Trả lời:

List<Integer> common = list1.stream()
                            .filter(list2::contains)
                            .collect(Collectors.toList());

29. Làm thế nào để sử dụng NavigableMap để tìm khoảng giá trị?

Trả lời:
NavigableMap hỗ trợ truy vấn phạm vi key:

NavigableMap<Integer, String> map = new TreeMap<>();
map.put(10, "Low");
map.put(20, "Medium");
map.put(30, "High");
System.out.println(map.subMap(15, true, 25, true)); // Kết quả: {20=Medium}

30. Làm thế nào để sử dụng EnumSet để quản lý quyền người dùng?

Trả lời:

enum Permission { READ, WRITE, EXECUTE }
EnumSet<Permission> permissions = EnumSet.of(Permission.READ, Permission.WRITE);
System.out.println(permissions.contains(Permission.READ)); // true

31. Làm thế nào để chuyển đổi một List thành Set để loại bỏ trùng lặp?

Trả lời:

Set<Integer> set = new HashSet<>(list);

32. Làm thế nào để nhóm dữ liệu theo một tiêu chí với Stream API?

Trả lời:

Map<Integer, List<String>> grouped = list.stream()
                                         .collect(Collectors.groupingBy(String::length));

33. Sử dụng Deque để triển khai undo/redo trong một ứng dụng.

Trả lời:

Deque<String> undoStack = new ArrayDeque<>();
Deque<String> redoStack = new ArrayDeque<>();
undoStack.push("Action 1");
undoStack.push("Action 2");
redoStack.push(undoStack.pop()); // Undo

34. Làm thế nào để kiểm tra List có thứ tự tăng dần không?

Trả lời:

boolean isSorted = IntStream.range(0, list.size() - 1)
                            .allMatch(i -> list.get(i) <= list.get(i + 1));

35. Làm thế nào để chuyển đổi một Set thành List giữ thứ tự?

Trả lời:

List<Integer> list = new ArrayList<>(set);

36. Làm thế nào để sử dụng ConcurrentHashMap cho bộ đếm số lần xuất hiện?

Trả lời:

ConcurrentHashMap<String, Integer> counter = new ConcurrentHashMap<>();
list.parallelStream().forEach(e -> counter.merge(e, 1, Integer::sum));

37. Làm thế nào để tạo một Collection immutable từ danh sách hiện tại?

Trả lời:

List<Integer> immutableList = Collections.unmodifiableList(list);

38. Sử dụng Stream API để lọc phần tử trong danh sách.

Trả lời:

List<Integer> filtered = list.stream().filter(e -> e > 10).collect(Collectors.toList());

39. Làm thế nào để tính tổng các giá trị trong List số nguyên?

Trả lời:

int sum = list.stream().mapToInt(Integer::intValue).sum();

40. Làm thế nào để phân vùng danh sách thành hai nhóm dựa trên điều kiện?

Trả lời:

Map<Boolean, List<Integer>> partitioned = list.stream()
                                              .collect(Collectors.partitioningBy(e -> e > 10));

41. Sử dụng TreeSet để tìm phần tử gần nhất lớn hơn một giá trị cho trước.

Trả lời:

TreeSet<Integer> set = new TreeSet<>(List.of(10, 20, 30));
System.out.println(set.higher(15)); // Kết quả: 20

42. Làm thế nào để xóa các phần tử trùng lặp trong danh sách giữ thứ tự?

Trả lời:

List<Integer> distinct = list.stream().distinct().collect(Collectors.toList());

43. Làm thế nào để tạo một Map từ hai List?

Trả lời:

Map<String, Integer> map = IntStream.range(0, keys.size())
                                    .boxed()
                                    .collect(Collectors.toMap(keys::get, values::get));

44. Làm thế nào để chuyển đổi một Collection thành mảng?

Trả lời:

Integer[] array = collection.toArray(new Integer[0]);

45. Làm thế nào để lọc chỉ giữ các phần tử duy nhất trong Map theo giá trị?

Trả lời:

Map<String, Integer> uniqueValues = map.entrySet()
                                       .stream()
                                       .filter(e -> Collections.frequency(map.values(), e.getValue()) == 1)
                                       .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

46. Sử dụng Stream API để tìm giá trị lớn nhất trong một List số nguyên.

Trả lời:

int max = list.stream().mapToInt(Integer::intValue).max().orElseThrow();

47. Sử dụng BlockingQueue để quản lý hàng đợi đa luồng.

Trả lời:

BlockingQueue<String> queue = new LinkedBlockingQueue<>();
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(() -> queue.add("Task 1"));
executor.execute(() -> System.out.println(queue.take()));
executor.shutdown();

48. Làm thế nào để tìm key có giá trị nhỏ nhất trong Map?

Trả lời:

String minKey = map.entrySet()
                   .stream()
                   .min(Map.Entry.comparingByValue())
                   .get()
                   .getKey();

49. Làm thế nào để tạo một Set chỉ chứa các phần tử xuất hiện một lần trong List?

Trả lời:

Set<Integer> unique = list.stream()
                          .filter(e -> Collections.frequency(list, e) == 1)
                          .collect(Collectors.toSet());

50. Sử dụng LinkedHashMap để duy trì thứ tự truy cập.

Trả lời:

LinkedHashMap<Integer, String> map = new LinkedHashMap<>(16, 0.75f, true);
map.put(1, "A");
map.put(2, "B");
map.get(1); // Thứ tự: 2 -> 1
System.out.println(map);

Phần 3: Hiệu suất và tối ưu hóa Collection (20 câu)

51. Làm thế nào để tối ưu hóa bộ nhớ khi sử dụng ArrayList?

Trả lời:

  • Đặt kích thước khởi tạo (initialCapacity) phù hợp để giảm số lần mở rộng.

  • Xóa các phần tử không cần thiết bằng trimToSize().


52. Cách chọn giữa ArrayList và LinkedList để tối ưu hiệu suất?

Trả lời:

  • Dùng ArrayList khi cần truy cập ngẫu nhiên nhanh.

  • Dùng LinkedList khi cần thêm/xóa ở đầu hoặc giữa danh sách thường xuyên.


53. Khi nào nên sử dụng HashSet thay vì TreeSet?

Trả lời: Dùng HashSet khi không cần duy trì thứ tự và ưu tiên hiệu suất cao hơn.


54. Tại sao nên cấu hình initialCapacity trong HashMap?

Trả lời: Để giảm số lần rehash và tăng hiệu suất khi làm việc với lượng dữ liệu lớn.


55. loadFactor trong HashMap có vai trò gì?

Trả lời: Xác định mức độ đầy tối đa của bảng băm trước khi tăng kích thước (rehash). Giá trị mặc định là 0.75.


56. Làm thế nào để giảm xung đột (collision) trong HashMap?

Trả lời:

  • Tạo hashCode() tốt để phân tán key đồng đều.

  • Tăng initialCapacity để giảm số phần tử trong mỗi bucket.


57. Hiệu suất của ConcurrentHashMap so với HashMap thế nào?

Trả lời: ConcurrentHashMap sử dụng khóa phân đoạn, nên hiệu suất tốt hơn trong môi trường đa luồng, nhưng chậm hơn HashMap trong đơn luồng.


58. So sánh hiệu suất giữa Vector và ArrayList.

Trả lời:

  • ArrayList nhanh hơn vì không đồng bộ hóa.

  • Vector đồng bộ hóa, phù hợp với môi trường đa luồng nhưng chậm hơn trong đơn luồng.


59. Làm thế nào để tăng hiệu suất duyệt qua List?

Trả lời:

  • Sử dụng vòng lặp for thay vì Iterator nếu danh sách hỗ trợ truy cập ngẫu nhiên (ArrayList).

  • Dùng ListIterator cho các danh sách như LinkedList.


60. Khi nào nên sử dụng ConcurrentSkipListMap?

Trả lời: Khi cần một Map thread-safe và duy trì thứ tự key (sắp xếp tự nhiên hoặc Comparator).


61. Tại sao CopyOnWriteArrayList phù hợp cho thao tác đọc nhiều, ghi ít?

Trả lời: Vì nó tạo bản sao mới khi ghi, đảm bảo thread-safe mà không gây xung đột với các luồng đang đọc.


62. Làm thế nào để kiểm soát hiệu suất của PriorityQueue?

Trả lời:

  • Sử dụng Comparator tùy chỉnh để tối ưu hóa thứ tự ưu tiên.

  • Đặt kích thước ban đầu phù hợp nếu biết trước số lượng phần tử.


63. Khi nào TreeMap chậm hơn HashMap?

Trả lời: TreeMap chậm hơn khi chèn hoặc tìm kiếm do cần duy trì cấu trúc cây, trong khi HashMap chỉ cần truy cập bằng bảng băm.


64. Tại sao LinkedHashMap phù hợp để xây dựng cache?

Trả lời: Vì LinkedHashMap duy trì thứ tự truy cập, cho phép xây dựng cache LRU (Least Recently Used).


65. Làm thế nào để tối ưu hóa bộ nhớ khi sử dụng HashSet?

Trả lời:

  • Đặt kích thước khởi tạo phù hợp.

  • Sử dụng các khóa với hashCode() hiệu quả để giảm xung đột.


66. Làm thế nào để đo hiệu suất của một Collection trong Java?

Trả lời: Sử dụng System.nanoTime() để đo thời gian thực hiện các thao tác như thêm, xóa, tìm kiếm trên Collection.


67. Khi nào nên sử dụng ArrayDeque thay vì LinkedList?

Trả lời: Khi cần hiệu suất cao cho thao tác hàng đợi hoặc stack, vì ArrayDeque nhanh hơn LinkedList.


68. Sự khác biệt về hiệu suất giữa synchronizedList và CopyOnWriteArrayList?

Trả lời:

  • synchronizedList: Đồng bộ hóa toàn bộ danh sách, gây tắc nghẽn khi có nhiều thao tác đồng thời.

  • CopyOnWriteArrayList: Chỉ tạo bản sao khi ghi, phù hợp khi đọc nhiều, ghi ít.


69. Làm thế nào để giảm chi phí rehash trong HashMap khi thêm phần tử lớn?

Trả lời: Đặt initialCapacity đủ lớn để tránh phải mở rộng bảng băm.


70. So sánh hiệu suất giữa HashSet và LinkedHashSet.

Trả lời:

  • HashSet: Nhanh hơn do không duy trì thứ tự.

  • LinkedHashSet: Chậm hơn vì phải duy trì thứ tự chèn.


71. Cách cải thiện hiệu suất khi duyệt Map lớn?

Trả lời: Sử dụng entrySet() để duyệt cả key và value thay vì keySet() (vì keySet() phải tra cứu giá trị cho mỗi key).


72. Khi nào nên sử dụng EnumSet để thay thế HashSet?

Trả lời: Khi làm việc với Enum, vì EnumSet sử dụng bit vector để lưu trữ dữ liệu, hiệu quả hơn HashSet.


Phần 4: Bài toán Stream API nâng cao (20 câu)

73. Làm thế nào để nhóm các phần tử trong một List theo độ dài chuỗi?

Trả lời:

Map<Integer, List<String>> grouped = list.stream()
                                         .collect(Collectors.groupingBy(String::length));

74. Làm thế nào để tính tổng giá trị của các số trong một List?

Trả lời:

int sum = list.stream().mapToInt(Integer::intValue).sum();

75. Làm thế nào để tạo một List chỉ chứa các phần tử duy nhất?

Trả lời:

List<Integer> distinctList = list.stream().distinct().collect(Collectors.toList());

76. Làm thế nào để phân vùng các phần tử trong List thành hai nhóm?

Trả lời:

Map<Boolean, List<Integer>> partitioned = list.stream()
                                              .collect(Collectors.partitioningBy(e -> e > 10));

77. Làm thế nào để lọc các phần tử bắt đầu bằng chữ cái 'A'?

Trả lời:

List<String> filtered = list.stream().filter(e -> e.startsWith("A")).collect(Collectors.toList());

78. Làm thế nào để sắp xếp một List theo thứ tự giảm dần?

Trả lời:

List<Integer> sorted = list.stream()
                           .sorted(Comparator.reverseOrder())
                           .collect(Collectors.toList());

79. Làm thế nào để chuyển đổi một List thành Map với key là phần tử và value là độ dài của chuỗi?

Trả lời:

Map<String, Integer> map = list.stream()
                                .collect(Collectors.toMap(e -> e, String::length));

80. Làm thế nào để đếm số lần xuất hiện của mỗi phần tử trong List?

Trả lời:

Map<String, Long> frequency = list.stream()
                                  .collect(Collectors.groupingBy(e -> e, Collectors.counting()));

81. Làm thế nào để tìm giá trị lớn nhất trong một List số nguyên?

Trả lời:

Optional<Integer> max = list.stream().max(Comparator.naturalOrder());

82. Làm thế nào để tìm phần tử đầu tiên thỏa mãn điều kiện trong List?

Trả lời:

Optional<Integer> result = list.stream().filter(e -> e > 10).findFirst();

83. Làm thế nào để chuyển đổi Stream thành List?

Trả lời:

List<Integer> resultList = stream.collect(Collectors.toList());

84. Làm thế nào để giới hạn số lượng phần tử trong Stream?

Trả lời:

List<Integer> limited = list.stream().limit(5).collect(Collectors.toList());

85. Làm thế nào để bỏ qua một số phần tử đầu tiên trong Stream?

Trả lời:

List<Integer> skipped = list.stream().skip(3).collect(Collectors.toList());

86. Làm thế nào để nối các chuỗi trong List thành một chuỗi duy nhất, cách nhau bởi dấu phẩy?

Trả lời:

String joined = list.stream().collect(Collectors.joining(", "));

87. Làm thế nào để chuyển đổi List thành Map theo một tiêu chí tùy chỉnh?

Trả lời:

Map<Integer, String> map = list.stream()
                               .collect(Collectors.toMap(String::length, e -> e, (v1, v2) -> v1));

88. Làm thế nào để chuyển đổi tất cả các phần tử trong List thành chữ in hoa?

Trả lời:

List<String> upperCaseList = list.stream().map(String::toUpperCase).collect(Collectors.toList());

89. Làm thế nào để chuyển một danh sách các List thành một danh sách phẳng (flattened list)?

Trả lời:

List<Integer> flattened = listOfLists.stream()
                                     .flatMap(List::stream)
                                     .collect(Collectors.toList());

90. Làm thế nào để tạo một Stream vô hạn và lấy 10 phần tử đầu tiên?

Trả lời:

List<Integer> generated = Stream.iterate(1, n -> n + 1)
                                 .limit(10)
                                 .collect(Collectors.toList());

91. Làm thế nào để kiểm tra tất cả các phần tử trong List đều thỏa mãn một điều kiện?

Trả lời:

boolean allMatch = list.stream().allMatch(e -> e > 0);

92. Làm thế nào để kiểm tra nếu ít nhất một phần tử thỏa mãn điều kiện?

Trả lời:

boolean anyMatch = list.stream().anyMatch(e -> e > 10);

93. Làm thế nào để chuyển đổi một Map thành Stream?

Trả lời:

Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();

94. Làm thế nào để tạo Map từ hai List (key và value tương ứng)?

Trả lời:

Map<String, Integer> map = IntStream.range(0, keys.size())
                                    .boxed()
                                    .collect(Collectors.toMap(keys::get, values::get));

95. Làm thế nào để kiểm tra danh sách có chứa giá trị trùng lặp không?

Trả lời:

boolean hasDuplicates = list.size() != list.stream().distinct().count();

96. Làm thế nào để tạo Stream song song từ một List?

Trả lời:

Stream<Integer> parallelStream = list.parallelStream();

97. Làm thế nào để gộp các Map thành một Map duy nhất?

Trả lời:

Map<String, Integer> merged = Stream.of(map1, map2)
                                    .flatMap(m -> m.entrySet().stream())
                                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1));

98. Làm thế nào để lọc các giá trị null trong Stream?

Trả lời:

List<Integer> filtered = list.stream().filter(Objects::nonNull).collect(Collectors.toList());

99. Làm thế nào để chuyển đổi List thành một Set không trùng lặp?

Trả lời:

Set<Integer> set = list.stream().collect(Collectors.toSet());

100. Làm thế nào để đo hiệu suất của Stream API?

Trả lời:

long startTime = System.nanoTime();
list.stream().filter(e -> e > 10).collect(Collectors.toList());
long endTime = System.nanoTime();
System.out.println("Thời gian thực thi: " + (endTime - startTime) + " nanoseconds");

Phần 5: Các câu hỏi về thiết kế và mở rộng Collection (10 câu)

101. Làm thế nào để tạo một Collection tùy chỉnh kế thừa từ AbstractList?

Trả lời:
Kế thừa AbstractList và triển khai các phương thức get()size() tối thiểu:

class CustomList<E> extends AbstractList<E> {
    private final List<E> internalList = new ArrayList<>();
    @Override
    public E get(int index) {
        return internalList.get(index);
    }
    @Override
    public int size() {
        return internalList.size();
    }
    @Override
    public boolean add(E e) {
        return internalList.add(e);
    }
}

102. Làm thế nào để thiết kế một hệ thống xếp hạng sản phẩm sử dụng TreeSet?

Trả lời:
Sử dụng TreeSet với Comparator tùy chỉnh để sắp xếp theo điểm đánh giá:

class Product {
    String name;
    int rating;
    Product(String name, int rating) {
        this.name = name;
        this.rating = rating;
    }
}
TreeSet<Product> ranking = new TreeSet<>((p1, p2) -> Integer.compare(p2.rating, p1.rating));
ranking.add(new Product("A", 5));
ranking.add(new Product("B", 4));

103. Làm thế nào để tạo một Map với nhiều giá trị cho cùng một key?

Trả lời:
Sử dụng Map<K, List<V>>:

Map<String, List<Integer>> map = new HashMap<>();
map.computeIfAbsent("key1", k -> new ArrayList<>()).add(1);

104. Làm thế nào để thiết kế một hệ thống Cache với LinkedHashMap?

Trả lời:
Sử dụng LinkedHashMap với chế độ truy cập (accessOrder = true) để tạo cache kiểu LRU:

class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;
    LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }
}
LRUCache<Integer, String> cache = new LRUCache<>(3);
cache.put(1, "A");
cache.put(2, "B");
cache.put(3, "C");
cache.get(1); // Truy cập làm mới 1
cache.put(4, "D"); // Loại bỏ phần tử 2

105. Làm thế nào để xây dựng một hệ thống quản lý quyền với EnumSet?

Trả lời:
Sử dụng EnumSet để quản lý quyền nhanh chóng và hiệu quả:

enum Permission { READ, WRITE, EXECUTE }
EnumSet<Permission> userPermissions = EnumSet.of(Permission.READ, Permission.WRITE);
System.out.println(userPermissions.contains(Permission.EXECUTE)); // false

106. Thiết kế một Stack tùy chỉnh dựa trên ArrayDeque.

Trả lời:

class CustomStack<E> {
    private final Deque<E> deque = new ArrayDeque<>();
    public void push(E element) {
        deque.push(element);
    }
    public E pop() {
        return deque.pop();
    }
    public E peek() {
        return deque.peek();
    }
}
CustomStack<Integer> stack = new CustomStack<>();
stack.push(1);
stack.push(2);
System.out.println(stack.pop()); // 2

107. Làm thế nào để tạo một Comparator phức tạp để sắp xếp List theo nhiều tiêu chí?

Trả lời:
Sử dụng Comparator với thenComparing:

List<Product> products = Arrays.asList(new Product("A", 5, 10), new Product("B", 5, 20));
products.sort(Comparator.comparingInt((Product p) -> p.rating)
                        .thenComparingInt(p -> p.price));

108. Làm thế nào để tạo một Collection bất biến tùy chỉnh?

Trả lời:
Kế thừa AbstractCollection và ném UnsupportedOperationException trong các phương thức sửa đổi:

class ImmutableCollection<E> extends AbstractCollection<E> {
    private final List<E> list;
    ImmutableCollection(List<E> list) {
        this.list = Collections.unmodifiableList(new ArrayList<>(list));
    }
    @Override
    public Iterator<E> iterator() {
        return list.iterator();
    }
    @Override
    public int size() {
        return list.size();
    }
}

109. Làm thế nào để kiểm soát lỗi kiểu trong Collection với Collections.checkedCollection()?

Trả lời:
Sử dụng checkedCollection() để kiểm tra kiểu dữ liệu tại runtime:

Collection<String> checked = Collections.checkedCollection(new ArrayList<>(), String.class);
checked.add("valid"); // OK
checked.add((String) new Object()); // Ném ClassCastException

110. Làm thế nào để thiết kế một hệ thống đa luồng an toàn sử dụng ConcurrentHashMap?

Trả lời:
Sử dụng ConcurrentHashMap để quản lý dữ liệu an toàn trong môi trường đa luồng:

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(() -> map.put("A", 1));
executor.execute(() -> System.out.println(map.get("A")));
executor.shutdown();

Phần này tập trung vào các bài toán thiết kế và mở rộng Collection, phù hợp để áp dụng trong thực tế và phát triển phần mềm. Nếu bạn cần thêm phần mở rộng nào, hãy cho tôi biết!