Menghapus Integer dengan benar dari Daftar <Integer>


201

Ini perangkap yang bagus yang baru saja saya temui. Pertimbangkan daftar bilangan bulat:

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

Adakah tebakan berpendidikan tentang apa yang terjadi ketika Anda mengeksekusi list.remove(1)? Bagaimana dengan list.remove(new Integer(1))? Ini dapat menyebabkan beberapa bug jahat.

Apa cara yang tepat untuk membedakan remove(int index), yang menghilangkan elemen dari indeks yang diberikan dan remove(Object o), yang menghilangkan elemen dengan referensi, ketika berhadapan dengan daftar bilangan bulat?


Poin utama yang perlu dipertimbangkan di sini adalah yang @Nikita sebutkan - pencocokan parameter yang tepat lebih diutamakan daripada tinju otomatis.


11
A: masalah sebenarnya di sini adalah bahwa seseorang di Sun entah bagaimana berpikir memiliki kelas pembungkus (tidak berubah) di sekitar primitif itu cerdas dan kemudian seseorang berpikir bahwa memiliki tinju otomatis (lebih) lebih pintar ... DAN ORANG YANG TETAP MENGGUNAKAN API LFA DEFAULT KAPAN LEBIH BAIK LEBIH BAIK . Untuk banyak tujuan ada solusi yang jauh lebih baik daripada Arraylist baru <Integer> . Misalnya Trove menyediakan sesuatu yang menjadi TIntArrayList . Semakin banyak saya memprogram di Java (SCJP sejak 2001), semakin sedikit saya menggunakan kelas wrapper dan semakin saya menggunakan API yang dirancang dengan baik (Trove, Google, dll. Muncul di benak).
SyntaxT3rr0r

Jawaban:


231

Java selalu memanggil metode yang paling sesuai dengan argumen Anda. Tinju otomatis dan upcasting implisit hanya dilakukan jika tidak ada metode yang dapat dipanggil tanpa casting / tinju otomatis.

Antarmuka Daftar menetapkan dua metode penghapusan (harap perhatikan penamaan argumen):

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

Itu berarti bahwa list.remove(1)menghapus objek di posisi 1 dan remove(new Integer(1))menghilangkan kejadian pertama dari elemen yang ditentukan dari daftar ini.


110
Memilih nit: Integer.valueOf(1)adalah praktik yang lebih baik daripada new Integer(1). Metode statis dapat melakukan caching dan semacamnya, sehingga Anda akan mendapatkan kinerja yang lebih baik.
decitrig

Proposal Peter Lawrey lebih baik dan menghindari kreasi objek yang tidak perlu.
assylias

@assylias: Proposal Peter Lawrey melakukan hal yang persis sama dengan proposal decitrig, hanya kurang transparan.
Mark Peters

@MarkPeters Komentar saya tentang new Integer(1), tapi saya setuju Integer.valueOf(1)atau (Integer) 1setara.
assylias

68

Anda bisa menggunakan casting

list.remove((int) n);

dan

list.remove((Integer) n);

Tidak masalah jika n adalah int atau Integer, metode ini akan selalu memanggil yang Anda harapkan.

Menggunakan (Integer) natau Integer.valueOf(n)lebih efisien daripada new Integer(n)dua yang pertama dapat menggunakan cache Integer, sedangkan yang berikutnya akan selalu membuat objek.


2
alangkah baiknya jika Anda bisa menjelaskan mengapa itu terjadi :) [kondisi autoboxing ...]
Yuval Adam

Dengan menggunakan casting, Anda memastikan kompiler melihat tipe yang Anda harapkan. Dalam kasus pertama '(int) n' hanya bisa bertipe int dalam kasus kedua '(Integer) n' hanya bisa bertipe Integer . 'n' akan dikonversi / kotak / unboxed sesuai kebutuhan atau Anda akan mendapatkan kesalahan kompiler jika tidak bisa.
Peter Lawrey

10

Saya tidak tahu tentang cara yang 'pantas', tetapi cara yang Anda sarankan berfungsi dengan baik:

list.remove(int_parameter);

menghapus elemen pada posisi yang diberikan dan

list.remove(Integer_parameter);

menghapus objek yang diberikan dari daftar.

Itu karena VM pada awalnya mencoba untuk menemukan metode yang dideklarasikan dengan tipe parameter yang persis sama dan hanya kemudian mencoba autoboxing.


7

list.remove(4)adalah pasangan yang tepat list.remove(int index), sehingga akan dipanggil. Jika Anda ingin panggilan list.remove(Object)melakukan hal berikut: list.remove((Integer)4).


Terima kasih Petar, para (Integer)pemeran sederhana seperti yang Anda tulis di atas tampaknya menjadi pendekatan yang paling mudah bagi saya.
vikingsteve

Saat menggunakan pendekatan terakhir Anda, sepertinya mengembalikan boolean. Ketika mencoba untuk menumpuk banyak penghapusan, saya mendapatkan kesalahan yang tidak bisa saya panggil hapus pada boolean.
Bram Vanroy

4

Adakah tebakan berpendidikan tentang apa yang terjadi ketika Anda menjalankan list.remove (1)? Bagaimana dengan list.remove (Integer baru (1))?

Tidak perlu menebak. Kasing pertama akan menghasilkan List.remove(int)dipanggil, dan elemen pada posisi 1akan dihapus. Kasus kedua akan menghasilkan List.remove(Integer)dipanggil, dan elemen yang nilainya sama dengan Integer(1)akan dihapus. Dalam kedua kasus, kompiler Java memilih kelebihan pencocokan terdekat.

Ya, ada potensi kebingungan (dan bug) di sini, tetapi ini adalah kasus penggunaan yang tidak biasa.

Ketika kedua List.removemetode didefinisikan di Java 1.2, kelebihan tidak tidak ambigu. Masalahnya hanya muncul dengan pengenalan obat generik dan autoboxing di Jawa 1.5. Dalam pandangan belakang, akan lebih baik jika salah satu metode pemindahan diberi nama yang berbeda. Tapi sekarang sudah terlambat.


2

Perhatikan bahwa bahkan jika VM tidak melakukan hal yang benar, yang dilakukan, Anda masih bisa memastikan perilaku yang tepat dengan menggunakan fakta yang remove(java.lang.Object)beroperasi pada objek yang berubah-ubah:

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

"Solusi" ini melanggar kontrak dari equalsmetode ini, khususnya (dari Javadoc) "Ini simetris: untuk setiap nilai referensi non-null x dan y, x. Equals (y) harus mengembalikan true jika dan hanya jika y.equals ( x) mengembalikan true. " Karena itu tidak dijamin untuk bekerja pada semua implementasi List, karena setiap implementasi List diperbolehkan untuk menukar x dan y in x.equals(y)at will, karena Javadoc of Object.equalsmengatakan bahwa ini harus valid.
Erwin Bolwidt

1

Cukup saya suka mengikuti seperti yang disarankan oleh #decitrig dalam jawaban pertama komentar yang diterima.

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

Ini membantu saya. Terima kasih lagi #decitrig atas komentar Anda. Mungkin membantu untuk seseorang.


0

Nah di sini adalah triknya.

Mari kita ambil dua contoh di sini:

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);

  }

}

Sekarang mari kita lihat hasilnya:

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]

Sekarang mari kita menganalisis hasilnya:

  1. Ketika 3 dihapus dari koleksi itu memanggil remove()metode koleksi yang diambil Object osebagai parameter. Karenanya ia menghilangkan objek 3. Tetapi dalam objek arrayList ditimpa oleh indeks 3 dan karenanya elemen ke-4 dihapus.

  2. Dengan logika yang sama dari penghapusan objek null dihapus dalam kedua kasus di output kedua.

Jadi untuk menghapus angka 3yang merupakan objek, kita secara eksplisit perlu melewati 3 sebagai object.

Dan itu bisa dilakukan dengan casting atau pembungkus menggunakan kelas wrapper Integer.

Misalnya:

Integer removeIndex = Integer.valueOf("3");
collection.remove(removeIndex);
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.