2013-07-04 29 views
6
public static void main(String[] args) { 
    HashSet set = new HashSet(); 
    set.add(new StringBuffer("abc")); 
    set.add(new StringBuffer("abc")); 
    set.add(new StringBuffer("abc")); 
    set.add(new StringBuffer("abc")); 
    System.out.println(set); 
} 

Output:Một tập hợp trong java không bao giờ cho phép trùng lặp, nhưng phải mất các đối tượng StringBuffer với cùng một đối số. Tại sao?

[abc,abc,abc,abc] 

đây trong mã trên tôi đã thêm đối tượng của StringBuffer("abc") nhiều lần và Set thêm nó nhưng không bao giờ Set thêm bản sao.

+1

StringBuffer không ghi đè bằng()/hashCode(), do đó, mỗi thể hiện StringBuffer sẽ được thêm vào Set, bất kể nội dung của StringBuffer. – proko

+0

@proko Hàm hashCode() chỉ đảm bảo rằng nó trông trong nhóm thích hợp cho mục đó. Nó thực sự là phương thức equals() gây ra các bản sao. Nó sẽ là có thể (mặc dù không phải là rất performant) để tạo ra một lớp StringBuffer đã có chính xác cùng một hashCode, nhưng bình đẳng khác nhau, và họ sẽ được công nhận là cá nhân quá. – Lunivore

+0

@Lunivore: vâng tôi biết điều đó. Nhưng nhìn vào câu trả lời khác ở đây, có vẻ như quen thuộc để đề cập đến cả hai phương pháp cho bối cảnh này. – proko

Trả lời

1

Bạn có thấy phương thức equals() (hoặc thiếu nó) trong StringBuffer không? Có câu trả lời cho bạn.

Một tập hợp hoặc cho rằng vấn đề bất kỳ bộ sưu tập dựa trên băm nào phụ thuộc vào hợp đồng được tiếp xúc bằng phương thức equals() và hashcode() trên đối tượng cho đặc tính hành vi của chúng.

Trong trường hợp của bạn vì StringBuffer không ghi đè lên các phương thức này, mỗi thể hiện StringBuffer mà bạn tạo khác nhau, tức là StringBuffer mới ("abc") == new StringBuffer ("abc") sẽ trả về false.

Tôi tò mò là tại sao một người nào đó sẽ thêm StringBuffer vào một tập hợp.

13

StringBuffer does not override Object#equals() and Object#hashCode(), vì vậy danh tính của StringBuffer trường dựa không về nội dung của bộ đệm, nhưng theo địa chỉ của đối tượng trong bộ nhớ. *


* danh tính này dựa trên một địa chỉ trong bộ nhớ không được yêu cầu nghiêm ngặt bởi JLS, nhưng là hậu quả của việc thực hiện Object#hashCode() điển hình. Từ javadoc:

Càng nhiều càng tốt là hợp lý thực tế, phương thức hashCode định nghĩa bởi lớp Object không trở lại số nguyên phân biệt cho các đối tượng khác nhau. (Điều này thường được thực hiện bằng cách chuyển đổi địa chỉ nội bộ của đối tượng vào một số nguyên, nhưng kỹ thuật thực hiện điều này là không cần thiết bởi các ngôn ngữ lập trình Java ™.)

8

StringBuffer không ghi đè lên một trong hai equals hay hashCode - vì vậy mỗi đối tượng chỉ bằng chính nó.

Điều này có ý nghĩa là StringBuffer là rất nhiều "có thể thay đổi theo thiết kế" - và bình đẳng có thể gây ra vấn đề khi hai đối tượng có thể thay đổi được bằng nhau. Việc sử dụng các đối tượng có thể thay đổi như các phím trong bản đồ hoặc một phần của tập hợp có thể gây ra sự cố. Nếu bạn thay đổi một sau khi chèn vào bộ sưu tập, điều đó sẽ làm mất hiệu lực mục nhập trong bộ sưu tập vì mã băm có thể thay đổi. Ví dụ: trong bản đồ, bạn thậm chí sẽ không thể tìm kiếm giá trị với đối tượng cùng một đối tượng làm khóa, vì thử nghiệm đầu tiên là mã băm.

StringBuffer (và StringBuilder) được thiết kế là các đối tượng rất thoáng qua - tạo chúng, thêm vào chúng, chuyển đổi chúng thành chuỗi, sau đó bạn đã hoàn tất. Bất cứ khi nào bạn thấy mình thêm chúng vào bộ sưu tập, bạn cần phải lùi lại một bước và xem liệu nó có thực sự hợp lý hay không. Chỉ cần thỉnh thoảng nó có thể làm, nhưng thường chỉ khi bản thân bộ sưu tập được rút ngắn.

Bạn nên cân nhắc điều này trong mã của riêng mình khi ghi đè equalshashCode - rất hiếm khi có ý tưởng tốt để dựa trên mọi khía cạnh có thể thay đổi của đối tượng; nó làm cho lớp khó sử dụng hơn và có thể dễ dàng dẫn đến các lỗi tinh vi có thể mất nhiều thời gian để gỡ lỗi.

+1

+1: các đối tượng có thể thay đổi trong bộ (hoặc, thường là các khóa bản đồ) chỉ là một ý tưởng tồi. Khi một lớp thư viện chuẩn không ghi đè 'equals()' và 'hashCode()', đó là một gợi ý khá mạnh. –

0

Hai đối tượng StringBuffer là các đối tượng khác nhau mặc dù có cùng đối số. Do đó HashSet chỉ thêm StringBuffers thay vì bỏ qua các bản sao.

0

Bộ băm hoạt động với "nhóm". Nó lưu trữ các giá trị trong các "nhóm" đó theo mã băm của chúng. Một "xô" có thể có một số thành viên trong đó, tùy thuộc vào việc các thành viên đó có bình đẳng hay không, bằng cách sử dụng phương thức equals(Object).

Vì vậy, giả sử chúng ta xây dựng một bộ băm với 10 nhóm, vì mục đích của đối số và thêm các số nguyên 1, 2, 3, 5, 7, 11 và 13 vào nó. Mã băm cho một int chỉ là int. Chúng tôi kết thúc với một cái gì đó như thế này:

  • (trống)
  • 1, 11
  • 3, 13
  • (trống)
  • (trống)
  • (trống)
  • (trống)

Cách truyền thống để sử dụng bộ là xem và xem một thành viên có trong bộ đó không. Vì vậy, khi chúng ta nói, "Có 11 trong bộ này?" bộ băm sẽ modulo 11 lên 10, lấy 1 và tìm trong nhóm thứ hai (chúng tôi đang bắt đầu nhóm của chúng tôi với 0 tất nhiên).

Điều này làm cho nó thực sự, thực sự nhanh chóng để xem các thành viên thuộc về một tập hợp hay không. Nếu chúng tôi thêm một 11 khác, bộ băm sẽ xem liệu nó đã có ở đó chưa. Nó sẽ không thêm nó một lần nữa nếu nó được. Nó sử dụng phương pháp equals(Object) để xác định điều đó và tất nhiên, 11 bằng 11.

Mã băm cho chuỗi như "abc" phụ thuộc vào các ký tự trong chuỗi đó. Khi bạn thêm một chuỗi trùng lặp, "abc", bộ băm sẽ tìm trong nhóm bên phải và sau đó sử dụng phương thức equals(Object) để xem liệu thành viên đã có ở đó chưa. Phương thức equals(Object) cho chuỗi cũng phụ thuộc vào các ký tự, vì vậy "abc" bằng "abc".

Khi bạn sử dụng một StringBuffer, mặc dù, mỗi StringBuffer có một mã băm, và bình đẳng, dựa trên ID đối tượng của nó. Nó không ghi đè các phương thức cơ bản equals(Object)hashCode(), vì vậy mọi StringBuffer đều nhìn vào bộ băm giống như một đối tượng khác. Chúng không thực sự trùng lặp.

Khi bạn in StringBuffers vào đầu ra, bạn đang gọi phương thức toString() trên StringBuffers. Điều đó làm cho chúng trông giống như chuỗi trùng lặp, đó là lý do tại sao bạn nhìn thấy đầu ra đó.

Đây cũng là lý do rất quan trọng để ghi đè hashCode() nếu bạn ghi đè equals(Object), nếu không, Bộ sẽ nhìn vào nhóm không đúng và bạn nhận được hành vi rất kỳ lạ và không thể đoán trước!

1

Hầu hết các đối tượng có thể thay đổi được đều không cho rằng nếu chúng chứa cùng một dữ liệu giống nhau. Vì chúng có thể thay đổi, bạn có thể thay đổi nội dung bất cứ lúc nào. tức là bây giờ nó có thể giống nhau, nhưng không muộn hơn, hoặc bây giờ nó có thể khác, nhưng giống như sau này

BTW Bạn không nên sử dụng StringBuffer nếu StringBuilder là một tùy chọn. StringBuffer đã được thay thế hơn mười năm trước.

Các vấn đề liên quan