Set interface và cách sắp xếp đối tượng trong Java

Để tớ thử trả lời giúp cậu nhé! :slight_smile:

Blue_Manh:

Blue_Manh:

Sorted Set khi tạo đối tượng cũng phải tạo đối tượng TreeSet và Set cũng có thể làm vậy nên cũng có thể khai triển những phương thức đó nên tại sao cần dùng SortedSet ạ?

SortedSet là interface, không thể tạo đối tượng được đâu cậu :smiley:
SortedSet hiển nhiên được dùng để khai báo 1 đối tượng Set đã được sắp xếp rồi cậu :smiley:
Trong SortedSet interface có các method không có trong Set interface:

Vậy nên, nếu cậu cần dùng những method đó, cậu buộc phải khai báo với SortedTree.

SortedSet<Integer> sortedSet = new TreeSet<>();
int firstValue = sortedSet.first();
// now do some fancy stuff with firstValue...

Blue_Manh:

Blue_Manh:

Cho em hỏi là 2 cách khai báo trên có khác gì nhau không ạ?

Nếu cậu khai báo như vậy và dùng biến s trong 1 method nào đó, thường sẽ không có vấn đề gì cả.
Vấn đề xảy ra khi cậu cần khai báo parameter của method. Ví dụ như cậu phải viết method in ra nội dung của từng phần tử trong Set:

// Declaration using interface
public void printEachElement(Set<Integer> set) {
   // ...
}

// Declaration using concrete class
public void printEachElement(TreeSet<Integer> set) {
   // ...
}

Ở method phía trên, cậu có thể truyền bất cứ thể hiện nào của Set: HashSet, TreeSet,… mà method đều thực hiện được, mà không quan tâm tới cách nó được cài đặt (sử dụng Hash hay Tree). Ở method dưới, đáng tiếc chỉ có TreeSet được chấp nhận thôi.
Điều tương tự xảy ra nếu cậu sử dụng Set trong property. Ví dụ:

// Implementation using generic Set
public class SomeAwesomeMarketplace {
    private Set<Country> sellingCountries;

    public SomeAwesomeMarketplace(Set<Country> sellingCountries) {
       this.sellingCountries = sellingCountries;
   }
}

// Implementation using concrete TreeSet
public class SomeAwesomeMarketplace {
    private TreeSet<Country> sellingCountries;

    public SomeAwesomeMarketplace(TreeSet<Country> sellingCountries) {
       this.sellingCountries = sellingCountries;
   }
}

Ở implementation trên, cậu nhận mọi thể loại set, không quan tâm tới cách nó được cài đặt, nhưng ở dưới, chỉ có TreeSet được nhận. Giờ, trong tương lai, nếu cậu nhận ra HashSet sẽ tốt hơn cho việc lưu trữ, so với việc phải build ra 1 cây, cậu sẽ phải đổi toàn bộ TreeSet thành HashSet hoặc Set, thay vì cứ truyền HashSet vào constructor.

Cậu có thể đã nhận ra, từ 2 ví dụ trên, với 1 object, chúng ta quan tâm tới hành vi của nó (behavior) hơn là cách nó được cài đặt (how it’s implemented). Hành vi của đối tượng, được định nghĩa bằng các method (các dịch vụ / hành vi) mà object đó có, và nó luôn có ở trong interface (nếu code của cậu design tuân theo nguyên tắc Liskov).
Đó là lý do tại sao chúng ta khai báo mà sử dụng interface :wink:

Blue_Manh:

Blue_Manh:

sinh ra Sorted Set để làm gì ạ

Trong 1 số trường hợp, cậu cần cụ thể SortedSet. Ví dụ: cậu muốn tìm 1 danh sách tất cả các unique custome ID đã được sắp xếp trong 1 loạt các giao dịch ngân hàng, cách hiệu quả nhất hiển nhiên là dùng SortedSet, thay vì cậu dùng generic Set, hoặc sử dụng list + check contains + sort.

Blue_Manh:

Blue_Manh:

  • Cho em hỏi cách sắp xếp mảng giảm dần hay thêm các điều kiện sắp xếp trong Java ạ, và cách sắp xếp 1 list các đối tượng theo 1 trường biến thực thế nào đó thì làm cách nào ạ.

Khi sắp xếp, ngoài giải thuật sắp xếp, cậu cần cả tiêu chí so sánh.
Giải thuật sắp xếp thường do thư viện lo. Còn tiêu chí so sánh được tự định nghĩa theo các cách:

  • Đối tượng cần sắp xếp cài đặt Comparable interface.
  • Định nghĩa cách so sánh dùng Comparator interface.

Ví dụ dưới đây demo cách sắp xếp People theo tên và tuổi (nếu cùng tên, thì sắp xếp theo tuổi):

  @Value
  public class Person implements Comparable<Person> {
    int age;
    String name;
    
    @Override
    public int compareTo(Person that) {
      if(that == null) {
        return 1;
      }
      
      if(this.getName() == null && that.getName() == null) {
        return 0;
      }
      
      if(this.getName() == null) {
        return -1;
      }

      int nameComparisionResult = this.getName().compareTo(that.getName());
      if(nameComparisionResult != 0) {
        return nameComparisionResult;
      }
      
      return this.getAge() - that.getAge();
    }
  }
  
  @Test
  public void testSort() throws Exception {
    List<Person> people = asList(new Person(22, "Alan Smith"), new Person(12, "Alan Smith"), new Person(100, "Harry Potter"));
    
    // Using Collections class with Comparable interface implementation
    List<Person> peopleClone = new ArrayList<>(people);
    Collections.sort(peopleClone);
    System.out.println(peopleClone);
    
    // Using Collections class with Comparator
    peopleClone = new ArrayList<>(people);
    Collections.sort(peopleClone, (a, b) -> a.compareTo(b));
    System.out.println(peopleClone);
    
    // Use List's sort method
    peopleClone = new ArrayList<>(people);
    // sort in the reverse way
    peopleClone.sort((a, b) -> - a.compareTo(b));
    System.out.println(peopleClone);
    
    // Result:
    // [SomeTest.Person(age=12, name=Alan Smith), SomeTest.Person(age=22, name=Alan Smith), SomeTest.Person(age=100, name=Harry Potter)]
    // [SomeTest.Person(age=12, name=Alan Smith), SomeTest.Person(age=22, name=Alan Smith), SomeTest.Person(age=100, name=Harry Potter)]
    // [SomeTest.Person(age=100, name=Harry Potter), SomeTest.Person(age=22, name=Alan Smith), SomeTest.Person(age=12, name=Alan Smith)]
  }

Blue_Manh:

Blue_Manh:

Khi add các đối tượng vào TreeSet hay TreeMap thì chúng sẽ tự động sắp xếp, nhưng chúng sẽ so sánh các đối tượng để xếp chúng trước sau như thế nào ạ !

Như tớ có đề cập ở trên, cậu có 2 phương pháp định nghĩa chiến lược sắp xếp, tương ứng với 2 loại constructor dưới đây của TreeSet/TreeMap

Đây là javadoc của TreeSet nullary constructor:

Constructs a new, empty tree set, sorted according to the natural ordering of its elements. All elements inserted into the set must implement the Comparable interface…

Trong đó có đề cập các phần tử được insert vào set này phải implement Comparable interface.

Còn đây là javadoc của constructor public TreeSet(Comparator<? super E> comparator)

Constructs a new, empty tree set, sorted according to the specified comparator. All elements inserted into the set must be mutually comparable by the specified comparator: comparator.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the set. If the user attempts to add an element to the set that violates this constraint, the add call will throw a ClassCastException .

Ở đây, cậu cần truyền Comparator instance vào để định nghĩa chiến lược sắp xếp.

Dưới đây là ví dụ sử dụng:

  @Test
  public void testName() throws Exception {
    SortedSet<Person> people = new TreeSet<>();
    people.add(new Person(22, "Alan Smith"));
    people.add(new Person(12, "Alan Smith"));
    people.add(new Person(100, "Harry Potter"));
    System.out.println(people);
    
    // Result:
    // [SomeTest.Person(age=12, name=Alan Smith), SomeTest.Person(age=22, name=Alan Smith), SomeTest.Person(age=100, name=Harry Potter)]
  }

Hope it helps!