30/09/2018, 23:30

Cách sử dụng Set Interface trong Collections

Các anh/chị trong group cho em hỏi với ạ. Em mới học về Java được một thời gian, giờ em cần sử dụng Set interface để làm 1 bài tập tạo ra một class DictionaryWord gồm có 2 thuộc tính là String means và String word, trong class này phải override phương thức compareTo() để so sánh 2 đối tượng DictinonaryWord giống nhau. Với yêu cầu của đề bài là 2 đối tượng DictionaryWord đc định nghĩa giống nhau là nếu a.word=b.word. Em đã định nghĩa lại phương thức compareTo() trong class DictionaryWord rồi nhưng khi em dùng HashSet thì in ra danh sách đối tượng thì nó vẫn hiện 2 đối tượng trùng word. Còn e dùng TreeSet thì in ra đúng yêu cầu đề bài. Vậy anh/chị có thể nói rõ cho em hiểu rõ hơn về HashSet, TreeSet được không ạ?

Quân viết 01:35 ngày 01/10/2018

HashSet sử dụng hashcode để so sánh 2 đối tượng có giống nhau hay không, trong khi đó treeset lại sử dụng campareTo method để so sánh 2 đối tượng và sắp xếp chúng, bởi thế nên những concreate class có từ hash đều sử dụng hashcode như là key và đều không đảm bảo thứ tự của obj được add vào khi duyệt

Vu Tuan viết 01:43 ngày 01/10/2018

Em chưa hiểu về cái hashcode đó lắm, a có thể giải thích về cái đó cho e được k ạ? Ví dụ e muốn so sánh 2 đối tượng DictionaryWord ở trên nhưng e muốn dùng HashSet để xử lý thì khi override e phải xử lý chỗ đoạn int hashcode() như thế nào a?

hong duc viết 01:43 ngày 01/10/2018

HashSet

khi sử dụng HashSet hoặc các hash-base Collection hoặc Map như HashSet, LinkedHashSet, HashMap, HashTable, WeakHashMap thì nhất định phải Override hashcode, và khi Override hashCode cùng phải override equals để tuân theo luật chung

a.hashCode() == b.hashCode() // true
// thì
a.equals(b) // cũng phải true

điều này không nhất thiết phải tuân theo hoàn toàn, nhưng nếu không tuân theo thì sẽ không có lợi khi sử dụng hash tables như trong JavaDoc có nói:
``

  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.``
    , nên tốt nhất cứ làm theo luật này

đây là vd khi Override hashCode() và equals()

class Person{
      String name;
      int age;
      
     @Override
     public int hasCode(){
             int hash = 7;
             hash += 97 * hash + name.hashCode();
             hash += 97 * hash + Objects.hashCode(age);
             return hash;
     }

     @Override
     public boolean equals(Object obj){
           if(obj instanceof Person){
                 Person p = (Person) obj;
                 return p.name.equals(this.name) && p.age == this.age;
           }
           return false;
     }

}

đây là cách override hashCode() netbeans thường tự tạo ra, hai số 797 được dùng bởi vì nó là số nguyên tố, khi tính hash người ta thường dùng số nguyên tố để giảm số lượng các Object trùng hashCode với nhau

Một điều cần lưu ý khi sử dụng với hash-base collection và map là khi đã thêm đối tượng vào trong HashSet (hoặc thêm đối tượng như là key trong HashMap) thì không nên thay đổi các thuộc tính của đối tượng và điều đó sẽ dẫn đến kết quả hashcode được tính ra khác đi

VD:

Person p1 = new Person("p1",15);
Person p2 = new Person("p2",15);
HashSet<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);

set.contains(p1); // sẽ trả về true
p1.setName("p3");
set.contains(p1); // sẽ trả về false

ở đây khi add p1 vào HashSet thì p1 sẽ được tính hashcode lần đầu và lưu lại, và khi gọi set.contains(p1) thì p1 trả về hashcode giống với cái được lưu nên trả về true
Lúc sau khi đổi thuộc name của p1 khác đi và gọi set.contains(p1) thì lần này p1 sẽ tạo ra hashcode khác với cái được lưu nên sẽ trả về false mặc dù thật chất vẫn còn p1 trong set

Nếu bắc buộc phải đổi thuộc tính thì phải xóa nó rồi thay đổi và add nó lại

TreeSet

Sự khác nhau giữa TreeSet và HashSet là các đối tượng trong TreeSet được sắp xếp còn trong HashSet thì không

Trong HashSet thì dùng hashcode để so sánh, còn trong TreeSet thì dùng compareTo để so sánh

Khi sử dụng TreeSet một là phải đưa cho nó Comparator hoặc lớp của đối tượng nó lưu trữ phải implements Comparable interface và Override compareTo()

Và một điều nữa là TreeSet chậm hơn HashSet một chút, cho nên nếu muốn luôn theo hàng thẳng lối thì dùng TreeSet còn không thì dùng HashSet

Ben Nguyen viết 01:36 ngày 01/10/2018

Về bản chất lưu trữ các phần tử của HashSet dựa trên HasMap , HashMap là 1 lớp triển khai từ Map interface. Cơ chế lưu trữ của HashMap dựa trên mối tương giao của hashcode và equal(), Như 2 bạn trên giải thích là so sánh dựa trên hashcode là vẫn đang còn thiếu, mà nó phải phụ thuộc vào cả equal() nữa. Mình xin nói sơ qua 1 chút về hashcode và equal().
hashcode() là gì? được sử dụng để trả về đối tượng được băm theo theo 1 công thức nào đấy của java giá trị là 1 số kiểu int.
obj1.equal(obj2) được sử dụng để so sánh địa chỉ của 2 đối tượng được lưu trữ trong java.
Nếu 2 đối tượng equal() trả về true —> hashcode của 2 đối tượng là như nhau. Nhưng nếu hashcode của 2 đối tượng bằng nhua thì chưa chắc 2 đối tượng equal() trả về true.
Để hiểu được vấn đề bạn đang gặp phải, bạn cần phải hiểu sâu bên trong cơ chế lưu trữ.
http://www.java2blog.com/2014/02/how-hashmap-works-in-java.html Bạn tham khảo nguồn tài nguyên này sẽ giải đáp thắc mắc của bạn. Mình không tiện nói ở đây, vì nó khá là dài dòng, và khó hiểu. Sẽ mất kha khá thời gian để hiểu được vấn đề này đấy.

Đỗ Trung Quân viết 01:32 ngày 01/10/2018

obj1.equal(obj2) được sử dụng để so sánh địa chỉ của 2 đối tượng được lưu trữ trong java.

No, you were confused about == and equals. Not address, not hashcode but it behaves the same as ==. Because when you call equals. it will do 2 step.

  • The first test is == first (to make the process quickly it can be fairly fast when the objects are identical).
  • If they not refer to same object. It will check second step is test values.

So, if 2 variables refer to 2 different Object (different address) it will compare values and also return true if they have same values

==>> The return values true or false depends on compare Values

Ben Nguyen viết 01:42 ngày 01/10/2018

No. It depends on what data type you want to compare, by default Wrapper class and String class modify equals method compare by value (==). But if you want to compare 2 objects of User-define class by extend equals method of Object class , it’s difference. Although 2 object of User-define class have same value but they return false, because memory address is difference . So, if you want to compare 2 object by equals method you have to override equals method and modify it by compare value. As you know, Hashset class don’t allow duplicate insertion element right? , If you declare HashSet or HashSet , when you add element to HashSet , HashSet will overload duplicate element. But what happen if you pass a Generic type is User-define class? you have to override equals() and hashcode() and modify them to overload duplicate element. Right?

Đỗ Trung Quân viết 01:40 ngày 01/10/2018

Tiếng việt cho dễ. Mình không nói đến hashset hay treeset hoặc hashcode ở đây. Mà mình chỉ ra chỗ bạn nhầm trong comment của bạn thôi

obj1.equal(obj2) được sử dụng để so sánh địa chỉ của 2 đối tượng được lưu trữ trong java.

Là sai.

  String a = new String("Hello"); 
  String b = "Hello";
  a.equals(b) == true 
 // cùng hashcode, cùng values trả về true. Nhưng địa chỉ lưu giữa khác nhau.
 // 1 cái trên heap 1 cái trong string pool. Mặc dù pool cũng là 1 vùng trên heap. 
 // 2 object a và b  ở 2 địa chỉ khác nhau. -> So sánh values

Thứ 2

No. It depends on what data type you want to compare

Nếu data không cùng loại hoặc không convert được cho nhau thì làm sao mà compare values được?. Vì vậy equals cũng chỉ là so sánh values thôi

Ben Nguyen viết 01:44 ngày 01/10/2018

Data type ở đây mình muốn nói đến kiểu class chẵng hạn, bởi 1 class cũng là 1 data type. bạn cho rằng equals so sánh value , vậy bạn tạo ra 1 class và tạo ra 2 đối tượng có cùng các thuộc tính sau đó dùng equals xem có bằng nhau không?

Đỗ Trung Quân viết 01:46 ngày 01/10/2018

vậy bạn tạo ra 1 class và tạo ra 2 đối tượng có cùng các thuộc tính sau đó dùng equals xem có bằng nhau không?

Khi bạn so sánh data type kiểu class như vậy bạn cần override hàm equals(). Bởi vì nó thuộc java.lang.object không như ví dụ mình lấy java.lang.String có thể trực tiếp sử dụng.

Ps: Dĩ nhiên khi bạn đã modify cả equals() và hashcode() thì thực sự nó sẽ so sánh address giống như == vậy. Còn nếu chỉ có Equals thì nó chỉ so sánh values.

Ben Nguyen viết 01:45 ngày 01/10/2018

Đúng rồi bạn. Đây cũng là ý mình muốn nói, vì nó phụ thuộc vào loại kiểu dữ liệu nào khi nó so sánh bằng equal nữa.

Bài liên quan
0