Xóa đúng số nguyên khỏi Danh sách <Số nguyên>


201

Đây là một cạm bẫy tốt đẹp tôi vừa gặp phải. Xem xét danh sách các số nguyên:

List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);

Bất kỳ phỏng đoán giáo dục về những gì xảy ra khi bạn thực hiện list.remove(1)? Thế còn list.remove(new Integer(1))? Điều này có thể gây ra một số lỗi khó chịu.

Cách thích hợp để phân biệt giữa remove(int index), loại bỏ một phần tử khỏi chỉ mục đã cho và remove(Object o)loại bỏ một phần tử bằng tham chiếu, khi xử lý danh sách các số nguyên là gì?


Điểm chính cần xem xét ở đây là một @Nikita đã đề cập - việc khớp tham số chính xác được ưu tiên hơn so với tự động đấm bốc.


11
Trả lời: Vấn đề thực sự ở đây là ai đó ở Sun bằng cách nào đó nghĩ rằng có các lớp bao bọc (bất biến) xung quanh người nguyên thủy là thông minh và sau đó, có người nghĩ rằng việc đấm bốc tự động thậm chí còn thông minh hơn ... VÀ RATNG MỌI NGƯỜI SỬ DỤNG API KHI TỐT HƠN TRÊN EXIST . Đối với nhiều mục đích, có nhiều giải pháp tốt hơn so với Arraylist mới <Integer> . Ví dụ, Trove cung cấp cho mọi thứ một TIntArrayList . Tôi càng lập trình nhiều hơn trong Java (SCJP từ năm 2001), tôi càng ít sử dụng các lớp trình bao bọc và tôi càng sử dụng các API được thiết kế tốt (Trove, Google, v.v.).
Cú phápT3rr0r

Câu trả lời:


230

Java luôn gọi phương thức phù hợp nhất với đối số của bạn. Tự động đấm bốc và phát sóng ngầm chỉ được thực hiện nếu không có phương pháp nào có thể được gọi mà không cần đúc / tự động đấm bốc.

Giao diện Danh sách chỉ định hai phương thức loại bỏ (xin lưu ý cách đặt tên của các đối số):

  • remove(Object o)
  • remove(int index)

Điều đó có nghĩa là list.remove(1)loại bỏ đối tượng ở vị trí 1 và remove(new Integer(1))loại bỏ sự xuất hiện đầu tiên của phần tử được chỉ định khỏi danh sách này.


110
Chọn một nit: Integer.valueOf(1)là thực hành tốt hơn new Integer(1). Phương thức tĩnh có thể thực hiện lưu trữ và như vậy, do đó bạn sẽ có hiệu suất tốt hơn.
decitrig

Đề xuất của Peter Lawrey là tốt hơn và tránh những sáng tạo đối tượng không cần thiết.
assylias

@assylias: Đề xuất của Peter Lawrey thực hiện chính xác như đề xuất của decitrig, chỉ kém minh bạch.
Mark Peters

@MarkPeter Nhận xét của tôi là về new Integer(1), nhưng tôi đồng ý rằng Integer.valueOf(1)hoặc (Integer) 1tương đương.
assylias

68

Bạn có thể sử dụng đúc

list.remove((int) n);

list.remove((Integer) n);

Không có vấn đề gì nếu n là int hoặc Integer, phương thức sẽ luôn gọi theo cách bạn mong đợi.

Sử dụng (Integer) nhoặc Integer.valueOf(n)hiệu quả hơn so với new Integer(n)hai cái đầu tiên có thể sử dụng bộ đệm Integer, trong khi cái sau sẽ luôn tạo một đối tượng.


2
sẽ thật tuyệt nếu bạn có thể giải thích tại sao lại như vậy :) [điều kiện tự động ...]
Yuval Adam

Bằng cách sử dụng truyền, bạn đảm bảo trình biên dịch nhìn thấy loại bạn mong đợi. Trong trường hợp đầu tiên '(int) n' chỉ có thể là kiểu int trong trường hợp thứ hai '(Integer) n' chỉ có thể là kiểu Integer . 'n' sẽ được chuyển đổi / đóng hộp / bỏ hộp theo yêu cầu hoặc bạn sẽ gặp lỗi trình biên dịch nếu không thể.
Peter Lawrey

10

Tôi không biết về cách 'phù hợp', nhưng cách bạn đề xuất hoạt động tốt:

list.remove(int_parameter);

loại bỏ yếu tố tại vị trí nhất định và

list.remove(Integer_parameter);

loại bỏ đối tượng nhất định khỏi danh sách.

Đó là vì VM lúc đầu cố gắng tìm phương thức được khai báo với cùng loại chính xác và chỉ sau đó thử tự động hộp.


7

list.remove(4)là một kết hợp chính xác list.remove(int index), vì vậy nó sẽ được gọi. Nếu bạn muốn gọi hãy list.remove(Object)làm như sau : list.remove((Integer)4).


Cảm ơn Petar, một (Integer)diễn viên đơn giản như bạn đã viết ở trên dường như là cách tiếp cận dễ dàng nhất đối với tôi.
vikingsteve

Khi sử dụng phương pháp cuối cùng của bạn, nó dường như trả về một boolean. Khi cố gắng xếp chồng nhiều lần xóa, tôi gặp lỗi mà tôi không thể gọi gỡ bỏ trên boolean.
Bram Vanroy

4

Bất kỳ phỏng đoán có giáo dục về những gì xảy ra khi bạn thực hiện list.remove (1)? Điều gì về list.remove (số nguyên mới (1))?

Không cần phải đoán. Trường hợp đầu tiên sẽ dẫn đến List.remove(int)việc được gọi và phần tử tại vị trí 1sẽ bị xóa. Trường hợp thứ hai sẽ dẫn đến List.remove(Integer)việc được gọi và phần tử có giá trị bằng Integer(1)sẽ bị xóa. Trong cả hai trường hợp, trình biên dịch Java chọn quá tải khớp gần nhất.

Vâng, có khả năng gây nhầm lẫn (và lỗi) ở đây, nhưng đây là trường hợp sử dụng khá hiếm gặp.

Khi hai List.removephương thức được định nghĩa trong Java 1.2, tình trạng quá tải không mơ hồ. Vấn đề chỉ nảy sinh khi giới thiệu về generic và autoboxing trong Java 1.5. Trong tầm nhìn xa, sẽ tốt hơn nếu một trong những phương thức loại bỏ được đặt một tên khác. Nhưng bây giờ đã quá muộn.


2

Lưu ý rằng ngay cả khi VM không thực hiện đúng, như vậy, bạn vẫn có thể đảm bảo hành vi đúng bằng cách sử dụng thực tế remove(java.lang.Object)hoạt động trên các đối tượng tùy ý:

myList.remove(new Object() {
  @Override
  public boolean equals(Object other) {
    int k = ((Integer) other).intValue();
    return k == 1;
  }
}

"Giải pháp" này phá vỡ hợp đồng của equalsphương thức, cụ thể (từ Javadoc) "Nó là đối xứng: đối với mọi giá trị tham chiếu không null x và y, x.equals (y) sẽ trả về true nếu và chỉ khi y.equals ( x) trả về đúng. ". Vì vậy, nó không được đảm bảo để hoạt động trên tất cả các triển khai List, bởi vì bất kỳ triển khai nào của Danh sách đều được phép hoán đổi x và y x.equals(y)theo ý muốn, vì Javadoc Object.equalsnói rằng điều này sẽ hợp lệ.
Erwin Bolwidt

1

Đơn giản là tôi đã thích làm theo như đề xuất của #decitrig trong câu trả lời được chấp nhận đầu tiên.

list.remove(Integer.valueOf(intereger_parameter));

Điều này đã giúp tôi. Cảm ơn một lần nữa #decitrig cho nhận xét của bạn. Nó có thể giúp cho một số.


0

Vâng đây là mẹo.

Hãy lấy hai ví dụ ở đây:

public class ArrayListExample {

public static void main(String[] args) {
    Collection<Integer> collection = new ArrayList<>();
    List<Integer> arrayList = new ArrayList<>();

    collection.add(1);
    collection.add(2);
    collection.add(3);
    collection.add(null);
    collection.add(4);
    collection.add(null);
    System.out.println("Collection" + collection);

    arrayList.add(1);
    arrayList.add(2);
    arrayList.add(3);
    arrayList.add(null);
    arrayList.add(4);
    arrayList.add(null);
    System.out.println("ArrayList" + arrayList);

    collection.remove(3);
    arrayList.remove(3);
    System.out.println("");
    System.out.println("After Removal of '3' :");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

    collection.remove(null);
    arrayList.remove(null);
    System.out.println("");
    System.out.println("After Removal of 'null': ");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

  }

}

Bây giờ hãy xem đầu ra:

Collection[1, 2, 3, null, 4, null]
ArrayList[1, 2, 3, null, 4, null]

After Removal of '3' :
Collection[1, 2, null, 4, null]
ArrayList[1, 2, 3, 4, null]

After Removal of 'null': 
Collection[1, 2, 4, null]
ArrayList[1, 2, 3, 4]

Bây giờ hãy phân tích đầu ra:

  1. Khi 3 được xóa khỏi bộ sưu tập, nó gọi remove()phương thức của bộ sưu tập lấy Object otham số. Do đó nó loại bỏ các đối tượng 3. Nhưng trong đối tượng ArrayList, nó bị ghi đè bởi chỉ số 3 và do đó phần tử thứ 4 bị loại bỏ.

  2. Theo cùng một logic của việc loại bỏ đối tượng null được loại bỏ trong cả hai trường hợp ở đầu ra thứ hai.

Vì vậy, để loại bỏ số 3đó là một đối tượng, chúng ta sẽ cần phải vượt qua 3 như một object.

Và điều đó có thể được thực hiện bằng cách đúc hoặc gói bằng cách sử dụng lớp bao bọc Integer.

Ví dụ:

Integer removeIndex = Integer.valueOf("3");
collection.remove(removeIndex);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.