Bài 8: Custom Collections
I. Giới Thiệu
Trong Java, ngoài việc sử dụng các collection có sẵn, đôi khi chúng ta cần tạo các collection tùy chỉnh để đáp ứng các yêu cầu đặc biệt. Việc tạo ra các custom collection yêu cầu kế thừa từ các lớp cơ bản hoặc triển khai các interface của Collection Framework và cung cấp các phương thức cần thiết.
II. Kế Thừa Các Lớp Cơ Bản
Kế Thừa từ AbstractList
AbstractListcung cấp một triển khai cơ bản choListinterface, giúp chúng ta dễ dàng tạo ra các lớp con củaListmà không cần phải viết lại tất cả các phương thức.Ví dụ: Tạo một danh sách tùy chỉnh chỉ cho phép các phần tử là số chẵn.
import java.util.AbstractList; public class EvenList extends AbstractList<Integer> { private final List<Integer> list = new ArrayList<>(); @Override public Integer get(int index) { return list.get(index); } @Override public int size() { return list.size(); } @Override public boolean add(Integer element) { if (element % 2 == 0) { return list.add(element); } else { throw new IllegalArgumentException("Only even numbers are allowed"); } } } public class EvenListExample { public static void main(String[] args) { EvenList evenList = new EvenList(); evenList.add(2); evenList.add(4); // evenList.add(3); // This line will throw an exception System.out.println(evenList); // Output: [2, 4] } }Kế Thừa từ AbstractSet
AbstractSetcung cấp một triển khai cơ bản choSetinterface. Chúng ta có thể sử dụng lớp này để tạo ra các tập hợp tùy chỉnh.Ví dụ: Tạo một tập hợp tùy chỉnh chỉ cho phép các phần tử duy nhất theo chiều dài chuỗi của chúng.
import java.util.AbstractSet; import java.util.Iterator; import java.util.HashSet; import java.util.Set; public class UniqueLengthSet extends AbstractSet<String> { private final Set<String> set = new HashSet<>(); private final Set<Integer> lengths = new HashSet<>(); @Override public Iterator<String> iterator() { return set.iterator(); } @Override public int size() { return set.size(); } @Override public boolean add(String element) { if (lengths.add(element.length())) { return set.add(element); } else { throw new IllegalArgumentException("Strings of this length are already present"); } } } public class UniqueLengthSetExample { public static void main(String[] args) { UniqueLengthSet uniqueLengthSet = new UniqueLengthSet(); uniqueLengthSet.add("Hello"); uniqueLengthSet.add("World"); // uniqueLengthSet.add("Java"); // This line will throw an exception System.out.println(uniqueLengthSet); // Output: [Hello, World] } }
III. Triển Khai Các Interface Cơ Bản
Triển Khai List Interface
Để tạo một lớp tùy chỉnh triển khai
Listinterface, chúng ta cần cung cấp các phương thức cơ bản nhưget,size,add,remove, vàiterator.Ví dụ: Tạo một danh sách tùy chỉnh giới hạn kích thước tối đa.
import java.util.Iterator; import java.util.List; import java.util.ArrayList; public class BoundedList<E> implements List<E> { private final List<E> list = new ArrayList<>(); private final int maxSize; public BoundedList(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("Max size must be greater than 0"); } this.maxSize = maxSize; } @Override public boolean add(E element) { if (list.size() >= maxSize) { throw new IllegalStateException("List has reached its maximum size"); } return list.add(element); } // Các phương thức khác của List interface cần được triển khai... @Override public E get(int index) { return list.get(index); } @Override public int size() { return list.size(); } @Override public Iterator<E> iterator() { return list.iterator(); } // ... (triển khai các phương thức còn lại) @Override public boolean isEmpty() { return list.isEmpty(); } @Override public boolean contains(Object o) { return list.contains(o); } @Override public Object[] toArray() { return list.toArray(); } @Override public <T> T[] toArray(T[] a) { return list.toArray(a); } @Override public boolean remove(Object o) { return list.remove(o); } @Override public boolean containsAll(Collection<?> c) { return list.containsAll(c); } @Override public boolean addAll(Collection<? extends E> c) { if (list.size() + c.size() > maxSize) { throw new IllegalStateException("Adding collection exceeds maximum size"); } return list.addAll(c); } @Override public boolean addAll(int index, Collection<? extends E> c) { if (list.size() + c.size() > maxSize) { throw new IllegalStateException("Adding collection exceeds maximum size"); } return list.addAll(index, c); } @Override public boolean removeAll(Collection<?> c) { return list.removeAll(c); } @Override public boolean retainAll(Collection<?> c) { return list.retainAll(c); } @Override public void clear() { list.clear(); } @Override public E set(int index, E element) { return list.set(index, element); } @Override public void add(int index, E element) { if (list.size() >= maxSize) { throw new IllegalStateException("List has reached its maximum size"); } list.add(index, element); } @Override public E remove(int index) { return list.remove(index); } @Override public int indexOf(Object o) { return list.indexOf(o); } @Override public int lastIndexOf(Object o) { return list.lastIndexOf(o); } @Override public ListIterator<E> listIterator() { return list.listIterator(); } @Override public ListIterator<E> listIterator(int index) { return list.listIterator(index); } @Override public List<E> subList(int fromIndex, int toIndex) { return list.subList(fromIndex, toIndex); } } public class BoundedListExample { public static void main(String[] args) { BoundedList<String> boundedList = new BoundedList<>(3); boundedList.add("A"); boundedList.add("B"); boundedList.add("C"); // boundedList.add("D"); // This line will throw an exception System.out.println(boundedList); // Output: [A, B, C] } }Triển Khai Set Interface
Để tạo một lớp tùy chỉnh triển khai
Setinterface, chúng ta cần cung cấp các phương thức cơ bản nhưadd,remove,size, vàiterator.Ví dụ: Tạo một tập hợp tùy chỉnh chỉ cho phép các phần tử có độ dài chuỗi là số lẻ.
import java.util.AbstractSet; import java.util.Iterator; import java.util.HashSet; import java.util.Set; public class OddLengthSet extends AbstractSet<String> { private final Set<String> set = new HashSet<>(); @Override public Iterator<String> iterator() { return set.iterator(); } @Override public int size() { return set.size(); } @Override public boolean add(String element) { if (element.length() % 2 != 0) { return set.add(element); } else { throw new IllegalArgumentException("Only odd length strings are allowed"); } } } public class OddLengthSetExample { public static void main(String[] args) { OddLengthSet oddLengthSet = new OddLengthSet(); oddLengthSet.add("Hello"); oddLengthSet.add("World"); // oddLengthSet.add("Java"); // This line will throw an exception System.out.println(oddLengthSet); // Output: [Hello, World] } }
IV. Bài Tập Thực Hành
Tạo lớp danh sách tùy chỉnh chỉ cho phép các số nguyên dương.
Yêu cầu: Tạo lớp kế thừa từ
AbstractListvà kiểm tra điều kiện trước khi thêm phần tử.Ví dụ:
import java.util.AbstractList; import java.util
.List; import java.util.ArrayList;
public class PositiveIntegerList extends AbstractList { private final List list = new ArrayList<>();
@Override public Integer get(int index) { return list.get(index); }
@Override public int size() { return list.size(); }
@Override public boolean add(Integer element) { if (element > 0) { return list.add(element); } else { throw new IllegalArgumentException("Only positive integers are allowed"); } } }
public class PositiveIntegerListExample { public static void main(String[] args) { PositiveIntegerList positiveList = new PositiveIntegerList(); positiveList.add(5); positiveList.add(10); // positiveList.add(-3); // This line will throw an exception
System.out.println(positiveList); // Output: [5, 10] } } ```
Tạo lớp tập hợp tùy chỉnh chỉ cho phép các chuỗi bắt đầu bằng chữ cái 'A'.
Yêu cầu: Tạo lớp kế thừa từ
AbstractSetvà kiểm tra điều kiện trước khi thêm phần tử.Ví dụ:
import java.util.AbstractSet; import java.util.Iterator; import java.util.HashSet; import java.util.Set; public class AStartStringSet extends AbstractSet<String> { private final Set<String> set = new HashSet<>(); @Override public Iterator<String> iterator() { return set.iterator(); } @Override public int size() { return set.size(); } @Override public boolean add(String element) { if (element.startsWith("A")) { return set.add(element); } else { throw new IllegalArgumentException("Only strings starting with 'A' are allowed"); } } } public class AStartStringSetExample { public static void main(String[] args) { AStartStringSet aStartSet = new AStartStringSet(); aStartSet.add("Apple"); aStartSet.add("Apricot"); // aStartSet.add("Banana"); // This line will throw an exception System.out.println(aStartSet); // Output: [Apple, Apricot] } }
Tạo lớp map tùy chỉnh chỉ cho phép các khóa là chuỗi và giá trị là số nguyên dương.
Yêu cầu: Tạo lớp kế thừa từ
AbstractMapvà kiểm tra điều kiện trước khi thêm cặp khóa-giá trị.Ví dụ:
import java.util.AbstractMap; import java.util.Set; import java.util.HashSet; import java.util.Map; import java.util.HashMap; public class StringKeyPositiveValueMap extends AbstractMap<String, Integer> { private final Map<String, Integer> map = new HashMap<>(); @Override public Set<Entry<String, Integer>> entrySet() { return map.entrySet(); } @Override public Integer put(String key, Integer value) { if (value > 0) { return map.put(key, value); } else { throw new IllegalArgumentException("Only positive integer values are allowed"); } } } public class StringKeyPositiveValueMapExample { public static void main(String[] args) { StringKeyPositiveValueMap customMap = new StringKeyPositiveValueMap(); customMap.put("Apple", 10); customMap.put("Banana", 20); // customMap.put("Cherry", -5); // This line will throw an exception customMap.forEach((key, value) -> System.out.println(key + ": " + value)); // Output: // Apple: 10 // Banana: 20 } }
V. Kết Luận
Trong bài viết này, chúng ta đã khám phá cách tạo các collection tùy chỉnh trong Java bằng cách kế thừa các lớp cơ bản và triển khai các interface của Collection Framework. Hiểu rõ và sử dụng hiệu quả việc tạo các custom collection sẽ giúp bạn xây dựng các cấu trúc dữ liệu phù hợp với các yêu cầu đặc biệt trong ứng dụng của mình. Các ví dụ minh họa và bài tập thực hành sẽ giúp củng cố kiến thức và ứng dụng hiệu quả trong các dự án thực tế.
VI. Tài Liệu Tham Khảo
Java SE Documentation: Java Collections Framework
"Effective Java" by Joshua Bloch: Các nguyên tắc và best practices khi sử dụng Collection Framework.