2015-05-20 20 views
19

Tôi đã sau hai kịch bản:hành vi không mong muốn của ArrayList remove() trong Java

1. int giá trị như tham số

int intNum = 2; 
List<Integer> list = new ArrayList<Integer>(); 
list.add(1); 
list.add(2); 
list.add(3); 
list.remove(intNum); 
System.out.println(list.size()); 
// output: 2 

2. giá trị lâu như tham số

long longNum = 2; 
List<Integer> list = new ArrayList<Integer>(); 
list.add(1); 
list.add(2); 
list.add(3); 
list.remove(longNum); 
System.out.println(list.size()); 
// output: 3 

Tôi đang chuyển 2 làm giá trị trong cả hai trường hợp, nhưng tôi nhận được một giá trị kích thước khác của Danh sách. Lý do thực sự cho hành vi này là gì?

Properly removing an Integer from a List không giải thích về xây dựng-in kiểu dữ liệu có cùng một giá trị nhưng có hành vi khác nhau như hỏi trên

+0

Bây giờ bạn thấy rằng yếu tố list.remove (2) là loại bỏ tại chỉ số 2 và không số nguyên 2, bạn nên tạo một điểm đến rõ ràng chuyển sang "int" hoặc "Integer" hoặc bạn chỉ có nguy cơ gây nhầm lẫn cho bản thân. – Neil

+0

Đây có thể là một câu hỏi hay cho một cuộc phỏng vấn kỹ thuật. – beetstra

Trả lời

24

autoboxing

Phương pháp list.remove bị quá tải, và hai chữ ký khác nhau được sử dụng cho các mục đích khác nhau. Một, list.remove(int), xóa một item dựa trên chỉ số của nó, và một trong những khác, list.remove(Object), xóa một item dựa trên sự bình đẳng đối tượng. tình đầu tiên của bạn gây nên các loại đầu tiên, và ví dụ thứ hai của bạn (với một long longNum), gây nên loại thứ hai, autoboxing các long nguyên thủy đến một đối tượng java.lang.Long. Đây không phải là tương đương với java.lang.Integer giá trị (autoboxed) thêm vào danh sách, và do đó sẽ không loại bỏ bất cứ điều gì từ danh sách và kích thước sẽ vẫn giống nhau.

+7

Và lý do quá tải 'Object' được chọn trong trường hợp thứ hai là vì' long' không thể được chuyển thành 'int'. 'Quá tải' đối tượng là chỉ có thể áp dụng, – Arkadiy

+1

Ngay cả khi tình huống đầu tiên kích hoạt loại thứ hai, kết quả sẽ khác nhau. – xehpuk

5

Từ List.remove() documentation:

remove (int index) Loại bỏ các phần tử tại vị trí được chỉ định trong danh sách này (hoạt động tùy chọn).

xóa (Đối tượng o) Loại bỏ sự xuất hiện đầu tiên của thành phần được chỉ định từ danh sách này, nếu nó có mặt (hoạt động tùy chọn).

removeAll (Bộ sưu tập c) Xóa khỏi danh sách tất cả các thành phần của nó được chứa trong bộ sưu tập được chỉ định (hoạt động tùy chọn).

Nếu ví dụ thứ hai của bạn thực sự là dài, nó sẽ không bị xóa (vì nó sử dụng phương pháp xóa thứ hai).

4

Giao diện List chứa hai phương thức remove() - remove(Object)remove(int).

Việc thực hiện remove(Object) trong Java 6 như sau:

public boolean remove(Object o) { 
if (o == null) { 
     for (int index = 0; index < size; index++) 
    if (elementData[index] == null) { 
     fastRemove(index); 
     return true; 
    } 
} else { 
    for (int index = 0; index < size; index++) 
    if (o.equals(elementData[index])) { 
     fastRemove(index); 
     return true; 
    } 
    } 
return false; 
} 

Việc thực hiện remove(int) trong Java 6 là:

public E remove(int index) { 
RangeCheck(index); 

modCount++; 
E oldValue = (E) elementData[index]; 

int numMoved = size - index - 1; 
if (numMoved > 0) 
    System.arraycopy(elementData, index+1, elementData, index, 
      numMoved); 
elementData[--size] = null; // Let gc do its work 

return oldValue; 
} 

Trong ví dụ đầu tiên của bạn, bạn đang thực sự gọi remove(int) , loại bỏ đối tượng tại chỉ mục được chỉ định. Trong trường hợp này, bạn đã chỉ định chỉ mục 2, thực tế là giá trị "3".

Trong ví dụ thứ hai, bạn đang gọi phương thức remove(Object), vì không có phương thức remove(long)long sẽ không được chuyển đổi thành int. Dựa trên việc thực hiện phương thức remove(Object), nó sẽ tìm đối tượng bình đẳng. Vì danh sách của bạn chứa các đối tượng thuộc loại Integer và bạn đang cung cấp Long, sẽ không có đối tượng nào khớp với nó.

Các phương pháp sau đây có lẽ là một ví dụ tốt hơn về những gì đang xảy ra:

public static void main(String[] args) { 
    ArrayList<Integer> list; 

    System.out.println("Removing intNum"); 
    int intNum = 2; 
    list = new ArrayList<Integer>(); 
    list.add(1); 
    list.add(2); 
    list.add(3); 
    System.out.println("List = " + list); 
    list.remove(intNum); 
    System.out.println("List = " + list); 

    System.out.println("Removing longNum"); 
    long longNum = 2; 
    list = new ArrayList<Integer>(); 
    list.add(1); 
    list.add(2); 
    list.add(3); 
    System.out.println("List = " + list); 
    list.remove(longNum); 
    System.out.println("List = " + list); 
} 

Kết quả của mã này là:

Removing intNum 
List = [1, 2, 3] 
List = [1, 2] 
Removing longNum 
List = [1, 2, 3] 
List = [1, 2, 3] 
5

Hãy cẩn thận: Cái đầu tiên xóa Integer tại index = 2. Xem ArrayList.remove(int)

Cách thứ hai cố gắng xóa đối tượng có ArrayList.remove(Object), nhưng đối tượng bạn muốn xóa không tồn tại vì đó là đối tượng Long.

2

Khi bạn đang thực hiện list.remove(intNum);, nó đang thực hiện ký hiệu remove(int index); của lớp List, loại bỏ mục của chỉ mục đã cho.

Tuy nhiên khi bạn đã làm list.remove(longNum); (xem xét bạn có nghĩa là longNumlong), nó thực hiện các boolean remove(Object o); chữ ký của List lớp, trong đó kiểm tra nếu đối tượng có mặt trong danh sách, và nếu có loại bỏ nó.

Vì danh sách là danh sách Integer nên không thể tìm thấy đối tượng và không xóa bất kỳ thứ gì và đó là lý do kết quả thứ hai là 3, không có gì bị xóa.

2

Calling List.remove() với một đối số int sẽ khớp với chữ ký remove(int index) và các mặt hàng trên chỉ số list[2] sẽ được gỡ bỏ bất kể nếu danh sách có một mục có giá trị 2.

Calling List.remove Integer () với một cuộc tranh cãi dài sẽ gây ra các trình biên dịch để làm auto-boxing vào một đối tượng long và khớp với chữ ký remove(Object o). Danh sách này sẽ lặp, yêu cầu nếu o.equals (mỗi mục) và như đã đề cập trước đó, Long không làm Integer bằng nhau.

2

Danh sách trong Bộ sưu tập có hai phương pháp quá tải 1. công E remove (int index) 2. public boolean remove (Object o)

Trong trường hợp phương pháp thứ hai của bạn được gọi. kể từ khi bạn đang đi qua dài (ép kiểu ngầm để dài wrapper lớp để lớp Object).

Bây giờ List.remove (longNum) loại bỏ phần tử có chỉ số thấp nhất i sao cho (longNum == null? Get (i) == null: longNum.equals (get (i))) (nếu phần tử như vậy tồn tại).

Hiện tại trong trường hợp của bạn khi bộ đếm đến vị trí thứ hai (tức là i = 1). longNum.equals (get (1)) trả về false vì get (1) trả về đối tượng của java.lang.Integer với value = 2 và longNum là một thể hiện của java.lang.Long với value = 2. Vì vậy, bằng phương pháp trả về false do đó nó không xóa các phần tử.

Bạn có thể kiểm tra các loại giá trị theo Tên lớp của nó

System.out.println(" Element Value :"+list.get(1)); 
    System.out.println(" Element Class :"+list.get(1).getClass()); 
Các vấn đề liên quan