2017-08-24 19 views
5

Tôi có một Bản đồ.TreeMap iterator.remove() sửa đổi Mục nhập cuối cùng

Map<Integer,String> map = ... 

bản đồ có n yếu tố (cho phép lấy ví dụ này, những 9)

map.put(1,"one"); 
    map.put(2,"two"); 
    map.put(3,"three"); 
    map.put(4,"four"); 
    map.put(5,"five"); 
    map.put(6,"six"); 
    map.put(7,"seven"); 
    map.put(8,"eigth"); 
    map.put(9,"nine"); 

Bây giờ tôi muốn để lặp qua bản đồ này, và loại bỏ các yếu tố n-th sử dụng iterator.

private void remove(int num, final Map<Integer, String> map) { 

    Iterator<Map.Entry<Integer,String>> it = map.entrySet().iterator(); 
    Map.Entry<Integer,String> entry; 
    while(it.hasNext()){ 

    entry = it.next(); 

    if(Integer.valueOf(num).equals(entry.getKey())){ 
     it.remove(); 
     System.out.println(entry.getValue()); 
     // vs 
     // System.out.println(entry.getValue()); 
     // it.remove(); 
    } 
    } 
} 

Từ javadoc, tôi giả sử, ngữ nghĩa của loại bỏ được xác định rõ.

Nhưng tùy thuộc vào việc thực hiện bản đồ - ví dụ: HashMap vs TreeMap có sự khác biệt liệu it.remove() được thực hiện trước khi hoặc sauentry.getValue().

cho HashMaps map = new HashMap<>() hành vi này là

... 
remove(4, map); //output: four 
//or 
remove(5, map); //output: five 

cho TreeMap map = new TreeMap<>() hành vi này là cùng, khi tôi loại bỏ các mục hiện tại từ iterator sau Tôi đã truy cập nó:

System.out.println(entry.getValue()); 
it.remove(); 

kết quả trong

remove(4, map); //output: four 
//or 
remove(5, map); //output: five 

cho đến nay rất tốt, nhưng nếu tôi loại bỏ các yếu tố trước tôi truy cập vào các mục:

it.remove(); 
System.out.println(entry.getValue()); 

Đầu ra là bất ngờ

remove(4, map); //output: five !!! 
//or 
remove(5, map); //output: five ok 

Rõ ràng, it.remove() của TreeMap đổi các Entries, bởi vì TreeMap được tạo thành từ Entries và trình vòng lặp thực sự trả về các phần tử thực tế của bản đồ. Và tùy thuộc vào vị trí hiện tại trong cây, các tham chiếu bên trong của điểm nhập vào phần tử tiếp theo hoặc phần tử hiện tại (đã loại bỏ).

Nhưng tôi không chắc liệu đây có phải là lỗi hay không hoặc nếu điều này là cố ý. Nếu trường hợp sau là trường hợp, tôi tự hỏi về lý do đằng sau?

Edit: Source code của TreeMap iterator.remove()

+0

Tôi không nhận được hành vi bạn mô tả trong bản đồ ... bạn sử dụng phiên bản Java nào? Điều gì sẽ xảy ra nếu bạn xuất giá trị của mục nhập cả trước và sau khi xóa()? – daniu

+0

jdk1.8.0_121 ... nó có thể tái sản xuất với TreeMap chỉ với 3 mục (1,2,3), loại bỏ phần tử ở giữa (2). Nhưng nó không hiển thị trong HashMap –

+0

in nó ra trước và sau khi kết quả trong "hai, ba" –

Trả lời

2

Từ Map.Entry Javadoc:

hành vi của một mục bản đồ là undefined nếu bản đồ ủng hộ đã được sửa đổi sau khi nhập cảnh được trả về bởi các iterator, ngoại trừ thông qua hoạt động setValue vào mục bản đồ

Và từ Map.Entry.getValue Javadoc:

Nếu ánh xạ đã bị xóa khỏi bản đồ sao lưu (bằng thao tác xóa của trình vòng lặp), kết quả của cuộc gọi này không được xác định.

Gọi entry.getValue() sau it.remove() bị cấm. Java không hứa hẹn về những gì sẽ xảy ra nếu bạn thử nó. Bạn nên truy xuất giá trị trước khi xóa mục nhập.

0

Đây là một câu trả lời một phần, nhưng hy vọng sẽ đấm người khác tắt về cách đưa ra một câu trả lời chính xác hơn.

Giả sử, cuộc gọi bạn thực hiện cho Iterator#next() trả một cái nhìn ngồi trên đầu trang của bản đồ bên dưới,

Sau đó, nó có thể không là một bất ngờ mà chúng ta có thể nhận được một số hành vi không xác định khi cố gắng truy cập vào các giá trị sau mục nhập bản đồ cơ bản đã bị xóa khỏi bản đồ.

while (it.hasNext()) { 
    entry = it.next(); 

    if (Integer.valueOf(num).equals(entry.getKey())) { 
     it.remove(); 
     // the object which 'entry' points to is already removed 
     // what is this pointing to? 
     System.out.println(entry.getValue()); 
    } 
} 

Bằng cách quan sát của riêng bạn, hành vi có vẻ là Map thực hiện cụ thể, ngụ ý rằng việc thực hiện của bản đồ đặc biệt được tham gia vào hành vi được quan sát.

+0

Tôi có thể xóa câu trả lời này nếu bạn cảm thấy nó không thêm giá trị ở đây. Nhân tiện, nếu bạn có thể tìm thấy mã nguồn để thực hiện 'Iterator', bạn chỉ có thể có câu trả lời. –

+0

Có, tôi biết _why_ nó xảy ra, nhưng không phải lý do tại sao nó được thực hiện theo cách này và nếu hiệu quả là cố ý (IMO nó vi phạm hợp đồng giao diện). –

+0

Hợp đồng nói đâu sau khi xóa mục nhập, mục nhập vẫn phải hợp lệ? Ngoài ra, mà không nhìn thấy mã nguồn cho một trình vòng lặp bản đồ, làm sao bạn có thể biết tại sao nó lại xảy ra? –

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