Skip to main content

Command Palette

Search for a command to run...

Bài 8: Custom Collections

Published
7 min readView as Markdown

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.

     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.

     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.

     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ẻ.

     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ụ:

        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] } } ```

  1. 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ụ:

        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]
            }
        }
      
  2. 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ụ:

        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.

More from this blog

devngu

169 posts