# 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

1. **Kế Thừa từ AbstractList**
    
    `AbstractList` cung cấp một triển khai cơ bản cho `List` interface, giúp chúng ta dễ dàng tạo ra các lớp con của `List` mà 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.
    
    ```java
    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]
        }
    }
    ```
    
2. **Kế Thừa từ AbstractSet**
    
    `AbstractSet` cung cấp một triển khai cơ bản cho `Set` interface. 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.
    
    ```java
    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

1. **Triển Khai List Interface**
    
    Để tạo một lớp tùy chỉnh triển khai `List` interface, 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.
    
    ```java
    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]
        }
    }
    ```
    
2. **Triển Khai Set Interface**
    
    Để tạo một lớp tùy chỉnh triển khai `Set` interface, 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ẻ.
    
    ```java
    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

1. **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ừ `AbstractList` và kiểm tra điều kiện trước khi thêm phần tử.
        
    * Ví dụ:
        
        ```java
        import java.util.AbstractList;
        import java.util
        ```
        

.List; import java.util.ArrayList;

public class PositiveIntegerList extends AbstractList { private final List list = new ArrayList&lt;&gt;();

@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 &gt; 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\] } } \`\`\`

2. **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ừ `AbstractSet` và kiểm tra điều kiện trước khi thêm phần tử.
        
    * Ví dụ:
        
        ```java
        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]
            }
        }
        ```
        
3. **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ừ `AbstractMap` và kiểm tra điều kiện trước khi thêm cặp khóa-giá trị.
        
    * Ví dụ:
        
        ```java
        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](https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html)
    
* "Effective Java" by Joshua Bloch: Các nguyên tắc và best practices khi sử dụng Collection Framework.
