Mengapa Iterator dan ListIterator Java menunjukkan antar elemen?

9

The Javadoc untuk ListIterator mengatakan:

A ListIteratortidak memiliki elemen saat ini; posisi kursornya selalu terletak di antara elemen yang akan dikembalikan oleh panggilan ke previous()dan elemen yang akan dikembalikan oleh panggilan ke next().

Mengapa Java ListIteratorditerapkan untuk menunjuk antara elemen daripada elemen saat ini? Tampaknya ini membuat kode klien kurang dibaca ketika telah berulang kali memanggil getNext(), getPrevious(), jadi saya berasumsi harus ada alasan yang baik untuk pilihan.

Sebagai catatan, saya hanya menulis sedikit perpustakaan yang disebut peekable-arraylist yang meluas ArrayList, Iteratordan ListIteratoryang menyediakan peekAtNext()dan peekAtPrevious()metode diimplementasikan seperti:

  @Override public synchronized T peekAtNext() {
     T t = next();
     previous();
     return t;
  }
glenviewjeff
sumber
2
Ada iterator yang dapat diintip dalam kode perpustakaan Guava.google.com/p/guava-libraries
kevin cline
Kevin terima kasih! Saya memposting pertanyaan tindak lanjut untuk ini di SO: Apakah mungkin menggunakan ForwardingListIterator Guava dengan PeekingIterator?
glenviewjeff

Jawaban:

12

Sejauh yang saya tahu, alasannya dapat ditemukan di bagian javadoc yang tidak Anda kutip (penekanan di bawah tambang):

Sebuah iterator untuk daftar yang memungkinkan programmer untuk menelusuri daftar di kedua arah, memodifikasi daftar selama iterasi ...

Anda lihat, tujuan yang dimaksudkan adalah untuk memungkinkan penggunaan saat daftar sedang dimodifikasi. Kemungkinan modifikasi ternyata termasuk penghapusan elemen.

Sekarang pikirkan apa yang akan terjadi jika kita menghapus elemen yang akan current()untuk iterator - dengan asumsi iterator akan memiliki gagasan tentang elemen saat ini? Dalam konteks ini, cara untuk mengimplementasikannya tanpa gagasan tentang elemen saat ini cukup masuk akal bagi saya - karena dengan begitu, iterator tidak perlu khawatir tentang penghapusan elemen.


Hal ini penting untuk dicatat bahwa javadoc tidak memerlukan implementasi antarmuka untuk thread aman.

  • Karena itu, seseorang seharusnya tidak mengharapkan penanganan modifikasi yang benar dilakukan dari utas berbeda - untuk itu, implementasi harus menyediakan cara tambahan untuk menyinkronkan akses, jaminan visibilitas dll seperti yang ditentukan oleh Java Memory Model per JSR 133 .

Apa yang ListIterator sanggup, adalah menangani modifikasi yang dilakukan dari utas yang sama saat iterasi. Tidak semua iterator seperti itu, javadocs ConcurrentModificationException secara khusus memperingatkan tentang ini:

... Perhatikan bahwa pengecualian ini tidak selalu menunjukkan bahwa suatu objek telah diubah secara bersamaan oleh utas yang berbeda. Jika satu utas mengeluarkan urutan pemanggilan metode yang melanggar kontrak suatu objek, objek tersebut dapat membuang pengecualian ini. Misalnya, jika utas memodifikasi koleksi secara langsung ketika sedang mengulangi koleksi dengan iterator gagal-cepat, iterator akan melempar pengecualian ini ...

agas
sumber
1
Ini juga berarti Anda tidak perlu terpisah insertBeforedan insertAfter, meskipun itu bukan masalah besar seperti remove.
Karl Bielefeldt
1
Jadi, apakah masalah yang secara bersamaan dapat dihapus dan diakses oleh elemen saat ini? Bukankah ini masalah yang sepadan dengan panggilan serentak next()? remove()akan synchronizedseperti itu getCurrent(). Apakah saya melewatkan sesuatu?
glenviewjeff
@glenviewjeff itu pengamatan yang sangat bagus. Saya juga bertanya-tanya bagaimana CME dapat ditangani di sana - maksud yang sangat memungkinkan untuk modifikasi menimbulkan kekhawatiran seperti itu. Kira saya perlu menggali lebih dalam. Saya 99,99% yakin itu tidak dimaksudkan sebagai thread aman, mungkin itu hanya dapat menangani mod bersamaan yang dilakukan dari utas yang sama (tidak semua iterator mampu melakukannya, Anda tahu)
gnat
Jika Anda tertarik, lihat edit saya. Saya menerapkan dan mengemas ekstensi ArrayListuntuk mencapai ini.
glenviewjeff
@glenviewjeff menarik. Dalam implementasi Anda, apakah berikutnya () dan sebelumnya () juga disinkronkan? Saya bertanya karena jika tidak, maka utas lainnya dapat "menelusuri" beberapa gerakan tak terduga tepat di tengah arus Anda (), membuatnya berperilaku tidak seperti yang diharapkan klien ... Metode seperti add () mungkin perlu disinkronkan juga
nyamuk