2013-06-20 36 views
6

Tôi đã đọc câu hỏi này: Changing the elements in a set changes the 'equals' semanticsgiá trị thay đổi trong HashSet

Tuy nhiên, tôi không biết làm thế nào để giải quyết vấn đề mà tôi không thể thay đổi một mục trong HashSet và loại bỏ nó sau này.

Tôi có một số ví dụ sourcecode:

public static void main(String[] args) { 
    TestClass testElement = new TestClass("1"); 
    Set<TestClass> set = new HashSet<>(); 
    set.add(testElement); 
    printIt(testElement, set, "First Set"); 
    testElement.setS1("asdf"); 
    printIt(testElement, set, "Set after changing value"); 
    set.remove(testElement); 
    printIt(testElement, set, "Set after trying to remove value"); 
    testElement.setS1("1"); 
    printIt(testElement, set, "Set after changing value back"); 
    set.remove(testElement); 
    printIt(testElement, set, "Set removing value"); 
} 

private static void printIt(TestClass hullo, Set<TestClass> set, String message) { 
    System.out.println(message + " (hashCode is " + hullo.hashCode() + "):"); 
    for (TestClass testClass : set) { 
     System.out.println(" " + testClass.toString()); 
     System.out.println("  HashCode: " + testClass.hashCode()); 
     System.out.println("  Element is equal: " + hullo.equals(testClass)); 
    } 
} 

đâu TestClass chỉ là một POJO chứa một biến (cộng getter & setter) và có hashcode() và equals() thực hiện.

Đã có yêu cầu hiển thị phương thức equals() và hashcode() -. Đây là những autogenerated bởi eclipse:

@Override 
public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + ((s1 == null) ? 0 : s1.hashCode()); 
    return result; 
} 

@Override 
public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    TestClass other = (TestClass) obj; 
    if (s1 == null) { 
     if (other.s1 != null) 
      return false; 
    } else if (!s1.equals(other.s1)) 
     return false; 
    return true; 
} 

Kết quả như sau:

First Set (hashCode is 80): 
    TestClass [s1=1] 
     HashCode: 80 
     Element is equal: true 
Set after changing value (hashCode is 3003475): 
    TestClass [s1=asdf] 
     HashCode: 3003475 
     Element is equal: true 
Set after trying to remove value (hashCode is 3003475): 
    TestClass [s1=asdf] 
     HashCode: 3003475 
     Element is equal: true 
Set after changing value back (hashCode is 80): 
    TestClass [s1=1] 
     HashCode: 80 
     Element is equal: true 
Set removing value (hashCode is 80): 

Khi hashcode đã thay đổi, tôi không thể loại bỏ các giá trị từ HashSet. Như trong số linked question, tôi hiểu được lý do tại sao nó giống như vậy, nhưng tôi không biết cách xóa giá trị đã thay đổi. Có khả năng làm như vậy không?

+0

Bạn có thể đăng phương thức hashcode và equals không? – mabbas

+0

@mabbas đã chỉnh sửa. – looper

Trả lời

8

Bạn đang gặp sự cố vì các khóa trong bộ băm của bạn không phải là bất biến. Nếu bạn không có khóa bất biến, bạn sẽ mất tham chiếu đối tượng khóa gốc khi đã sửa đổi. Và sẽ không bao giờ có thể có được xử lý của điều đó, mà đôi khi được gọi là rò rỉ bộ nhớ trong bộ sưu tập. Vì vậy, nếu bạn sử dụng các phím bất biến, bạn sẽ không gặp phải tình huống này.

+0

Thú vị - là kết quả của bộ băm thực sự là một trình bao bọc xung quanh bản đồ băm? – robjohncox

+0

@robjohncox Đó là vì cách băm hoạt động. Hashcodes được sử dụng để lưu trữ và truy xuất các đối tượng. Giả sử bạn tạo một đối tượng quan trọng và khi bạn đặt nó vào một hashmap, phương thức hashcode của nó sẽ được gọi là caluculate băm và tìm thùng để lưu trữ. Khi bạn cố gắng lấy nó, hashcode được gọi để lấy hash/bucket nơi lưu trữ khóa. Nếu bạn thay đổi đối tượng khóa sau khi lưu trữ trong tập hợp/bản đồ thì phương thức hashcode sẽ trả về một giá trị băm khác cho đối tượng khóa đó, không giống như được sử dụng để lưu trữ khóa. –

+1

Rất tốt nhưng để tìm kiếm tham chiếu trong HashSet (hashmap) không chỉ hashcode được sử dụng mà còn bằng được sử dụng –

1

Khi bạn thêm testElement vào HashSet, nó sẽ chọn một nhóm dựa trên mã băm cho testElement. Khi bạn yêu cầu HashSet nếu nó có chứa một TestElement, nó tính toán mã băm của đối tượng mà nó đang tìm kiếm và chỉ tìm kiếm trong nhóm đó.

hashCode() của bạn dựa trên trường không phải cuối cùng, mã băm có thể thay đổi đằng sau hậu trường của HashSet. Do đó hoàn toàn vô hiệu hóa giả định cơ bản của HashSet.

Việc triển khai chính xác cho Testclass sẽ có trường s1 là cuối cùng.

2

Là câu hỏi bạn đã liên kết đến chi tiết và như những người khác đã chỉ ra, bạn đang gặp phải vấn đề chính có thể thay đổi. Tôi sẽ yêu cầu từ số Javadoc:

Lưu ý: Cần chú ý cẩn thận nếu đối tượng có thể thay đổi được sử dụng làm yếu tố . Hành vi của một tập hợp không được xác định nếu giá trị của một đối tượng được thay đổi theo cách ảnh hưởng đến bằng so sánh trong khi đối tượng là một phần tử trong tập hợp.

Như bạn đã chỉ ra, bạn hiểu điều đó. Câu hỏi là, làm thế nào để bạn thực sự loại bỏ các đối tượng cho rằng đó là trường hợp? Bạn không thể sử dụng Set.remove(), vì đối tượng của bạn bị mất trong bảng băm. Tuy nhiên, bạn có thể sử dụng Iterator để làm điều đó.Giống như sau:

TestClass toRemove = <the same instance, but mutated>; 
for (Iterator<TestClass> iter = set.iterator(); iter.hasNext();) { 
    TestClass item = iter.next(); 
    if (toRemove.equals(item)) { 
    iter.remove(); 
    } 
} 

Cách tiếp cận này dựa trên thực tế là tiêu chuẩn equals() phương pháp, giống như bạn đang sử dụng, có một kiểm tra ví dụ, và kiểm tra rằng sẽ trở thành sự thật.

Hãy nhớ rằng đây không phải là cách phải để giải quyết vấn đề này. Cách đúng là sử dụng khóa không thể thay đổi hoặc "tập thể dục chăm sóc tuyệt vời", nhưng đó là cách để xóa đối tượng bị đột biến khỏi HashSet.

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