Di Cocoa, jika saya ingin mengulang melalui NSMutableArray dan menghapus beberapa objek yang memenuhi kriteria tertentu, apa cara terbaik untuk melakukan ini tanpa memulai ulang loop setiap kali saya menghapus objek?
Terima kasih,
Sunting: Hanya untuk memperjelas - Saya sedang mencari cara terbaik, misalnya sesuatu yang lebih elegan daripada memperbarui secara manual indeks saya. Misalnya di C ++ yang bisa saya lakukan;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
objective-c
cocoa
Andrew Grant
sumber
sumber
Jawaban:
Untuk lebih jelasnya saya ingin membuat loop awal di mana saya mengumpulkan item untuk dihapus. Lalu saya menghapusnya. Berikut ini contoh menggunakan sintaks Objective-C 2.0:
Maka tidak ada pertanyaan tentang apakah indeks diperbarui dengan benar, atau detail pembukuan kecil lainnya.
Diedit untuk menambahkan:
Telah dicatat dalam jawaban lain bahwa formulasi terbalik harus lebih cepat. yaitu Jika Anda beralih melalui array dan menyusun array objek baru untuk disimpan, bukan objek untuk dibuang. Itu mungkin benar (walaupun bagaimana dengan memori dan biaya pemrosesan mengalokasikan array baru, dan membuang yang lama?) Tetapi bahkan jika lebih cepat itu mungkin bukan masalah besar seperti untuk implementasi naif, karena NSArrays jangan berperilaku seperti array "normal". Mereka bicara tetapi mereka berjalan dengan cara yang berbeda. Lihat analisis yang baik di sini:
Formulasi terbalik mungkin lebih cepat, tetapi saya tidak pernah perlu peduli apakah itu, karena formulasi di atas selalu cukup cepat untuk kebutuhan saya.
Bagi saya pesan untuk dibawa pulang adalah menggunakan formulasi apa pun yang paling jelas bagi Anda. Optimalkan hanya jika perlu. Saya pribadi menemukan formulasi di atas paling jelas, itulah sebabnya saya menggunakannya. Tetapi jika formulasi terbalik lebih jelas bagi Anda, lakukanlah.
sumber
Satu lagi variasi. Jadi Anda mendapatkan keterbacaan dan kinerja yang baik:
sumber
removeObjectsAtIndexes
adalah metode terburuk untuk menghapus objek, apakah Anda setuju dengan itu? Saya menanyakan ini karena jawaban Anda terlalu tua sekarang. Tetap bagus memilih yang terbaik?enumerateObjectsUsingBlock:
akan membuat Anda kenaikan indeks gratis.Ini adalah masalah yang sangat sederhana. Anda baru saja beralih mundur:
Ini adalah pola yang sangat umum.
sumber
Beberapa jawaban lain akan memiliki kinerja yang buruk pada array yang sangat besar, karena metode suka
removeObject:
danremoveObjectsInArray:
melibatkan melakukan pencarian linier pada penerima, yang merupakan pemborosan karena Anda sudah tahu di mana objek tersebut. Selain itu, setiap panggilan keremoveObjectAtIndex:
harus menyalin nilai dari indeks ke akhir array dengan satu slot pada suatu waktu.Yang lebih efisien adalah sebagai berikut:
Karena kami mengatur kapasitas
itemsToKeep
, kami tidak membuang waktu menyalin nilai selama pengubahan ukuran. Kami tidak mengubah array di tempat, jadi kami bebas menggunakan Penghitungan Cepat. MenggunakansetArray:
untuk mengganti kontenarray
denganitemsToKeep
akan efisien. Bergantung pada kode Anda, Anda bahkan dapat mengganti baris terakhir dengan:Jadi bahkan tidak perlu menyalin nilai, hanya menukar pointer.
sumber
Anda dapat menggunakan NSpredicate untuk menghapus item dari array yang bisa berubah-ubah. Ini tidak memerlukan loop.
Misalnya jika Anda memiliki NSMutableArray nama, Anda dapat membuat predikat seperti ini:
Baris berikut akan meninggalkan Anda dengan array yang hanya berisi nama yang dimulai dengan b.
Jika Anda kesulitan membuat predikat yang Anda butuhkan, gunakan tautan pengembang apel ini .
sumber
Saya melakukan tes kinerja menggunakan 4 metode berbeda. Setiap tes diulangi melalui semua elemen dalam array elemen 100.000, dan dihapus setiap item ke-5. Hasilnya tidak banyak berbeda dengan / tanpa optimasi. Ini dilakukan pada iPad 4:
(1)
removeObjectAtIndex:
- 271 ms(2)
removeObjectsAtIndexes:
- 1010 ms (karena membangun set indeks membutuhkan ~ 700 ms; jika tidak, ini pada dasarnya sama dengan memanggil removeObjectAtIndex: untuk setiap item)(3)
removeObjects:
- 326 ms(4) membuat array baru dengan objek yang lulus tes - 17 ms
Jadi, membuat array baru adalah yang tercepat. Metode lain semuanya sebanding, kecuali yang menggunakan removeObjectsAtIndexes: akan lebih buruk dengan lebih banyak item untuk dihapus, karena waktu yang dibutuhkan untuk membangun set indeks.
sumber
Gunakan loop yang menghitung mundur indeks:
atau buat salinan dengan objek yang ingin Anda simpan.
Secara khusus, jangan gunakan
for (id object in array)
loop atauNSEnumerator
.sumber
Untuk iOS 4+ atau OS X 10.6+, Apple menambahkan
passingTest
serangkaian APINSMutableArray
, seperti– indexesOfObjectsPassingTest:
. Solusi dengan API tersebut adalah:sumber
Saat ini Anda dapat menggunakan enumerasi berbasis blok terbalik. Contoh kode sederhana:
Hasil:
opsi lain hanya dengan satu baris kode:
sumber
Dengan cara yang lebih deklaratif, tergantung pada kriteria yang cocok dengan item yang ingin Anda hapus dapat digunakan:
@Nathan harusnya sangat efisien
sumber
Inilah cara yang mudah dan bersih. Saya suka menduplikasi array saya tepat di panggilan penghitungan cepat:
Dengan cara ini Anda menghitung melalui salinan array yang dihapus, keduanya memegang objek yang sama. NSArray menyimpan pointer objek hanya jadi ini benar-benar memori / kinerja bijaksana.
sumber
for (LineItem *item in self.lineItems.copy)
Tambahkan objek yang ingin Anda hapus ke array kedua dan, setelah loop, gunakan -removeObjectsInArray :.
sumber
ini harus dilakukan:
semoga ini membantu...
sumber
Mengapa Anda tidak menambahkan objek yang akan dihapus ke NSMutableArray lain. Setelah selesai iterasi, Anda dapat menghapus objek yang telah Anda kumpulkan.
sumber
Bagaimana dengan menukar elemen yang ingin Anda hapus dengan elemen 'n'th,' n-1'th elemen, dan sebagainya?
Setelah selesai, Anda mengubah ukuran array ke 'ukuran sebelumnya - jumlah swap'
sumber
Jika semua objek dalam array Anda adalah unik atau Anda ingin menghapus semua kemunculan objek saat ditemukan, Anda bisa menghitung dengan cepat pada salinan array dan menggunakan [NSMutableArray removeObject:] untuk menghapus objek dari aslinya.
sumber
+arrayWithArray
sedang dijalankan?Anwser benzado di atas adalah apa yang harus Anda lakukan untuk preformace. Dalam salah satu aplikasi saya, removeObjectsInArray membutuhkan waktu 1 menit, hanya dengan menambahkan array baru, 0,023 detik.
sumber
Saya mendefinisikan kategori yang memungkinkan saya memfilter menggunakan blok, seperti ini:
yang kemudian bisa digunakan seperti ini:
sumber
Implementasi yang lebih baik bisa menggunakan metode kategori di bawah ini pada NSMutableArray.
Blok predikat dapat diimplementasikan untuk melakukan pemrosesan pada setiap objek dalam array. Jika predikat mengembalikan true objek dihapus.
Contoh untuk array tanggal untuk menghapus semua tanggal yang ada di masa lalu:
sumber
Iterating mundur adalah favorit saya selama bertahun-tahun, tetapi untuk waktu yang lama saya tidak pernah menemukan kasus di mana objek 'terdalam' (jumlah tertinggi) pertama kali dihapus. Sesaat sebelum pointer bergerak ke indeks berikutnya tidak ada apa-apa dan itu crash.
Cara Benzado adalah yang paling dekat dengan apa yang saya lakukan sekarang tetapi saya tidak pernah menyadari akan ada perombakan tumpukan setelah setiap pemindahan.
di bawah Xcode 6 ini berfungsi
sumber