Cara Mengakses Penghitung Iterasi di Untuk Setiap Loop

1. Ikhtisar

Saat melakukan iterasi terhadap data di Java, kita mungkin ingin mengakses item saat ini dan posisinya di sumber data.

Ini sangat mudah dicapai dalam pengulangan for klasik , di mana posisi biasanya menjadi fokus kalkulasi pengulangan, tetapi memerlukan sedikit lebih banyak pekerjaan saat kita menggunakan konstruksi seperti untuk setiap pengulangan atau aliran.

Dalam tutorial singkat ini, kita akan melihat beberapa cara untuk setiap operasi dapat menyertakan penghitung.

2. Menerapkan Counter

Mari kita mulai dengan contoh sederhana. Kami akan mengambil daftar film yang dipesan dan menampilkannya beserta peringkatnya.

List IMDB_TOP_MOVIES = Arrays.asList("The Shawshank Redemption", "The Godfather", "The Godfather II", "The Dark Knight");

2.1. untuk Loop

Sebuah untuk lingkaran menggunakan counter untuk referensi item saat ini, sehingga cara mudah untuk beroperasi lebih baik data dan indeks dalam daftar:

List rankings = new ArrayList(); for (int i = 0; i < movies.size(); i++) { String ranking = (i + 1) + ": " + movies.get(i); rankings.add(ranking); }

Karena List ini mungkin adalah ArrayList , operasi get efisien, dan kode di atas adalah solusi sederhana untuk masalah kita.

assertThat(getRankingsWithForLoop(IMDB_TOP_MOVIES)) .containsExactly("1: The Shawshank Redemption", "2: The Godfather", "3: The Godfather II", "4: The Dark Knight");

Namun, tidak semua sumber data di Java dapat diulangi dengan cara ini. Terkadang get adalah operasi yang memakan waktu lama, atau kami hanya dapat memproses elemen berikutnya dari sumber data menggunakan Stream atau Iterable.

2.2. untuk Setiap Loop

Kami akan terus menggunakan daftar film kami, tetapi anggaplah kami hanya dapat mengulanginya menggunakan Java untuk setiap konstruksi:

for (String movie : IMDB_TOP_MOVIES) { // use movie value }

Di sini kita perlu menggunakan variabel terpisah untuk melacak indeks saat ini. Kita dapat membangunnya di luar loop, dan menambahkannya di dalam:

int i = 0; for (String movie : movies) { String ranking = (i + 1) + ": " + movie; rankings.add(ranking); i++; }

Kita harus mencatat bahwa kita harus menaikkan penghitung setelah digunakan dalam loop.

3. Fungsional untuk Masing - masing

Menulis ekstensi penghitung setiap kali kita membutuhkannya dapat mengakibatkan duplikasi kode dan dapat berisiko bug yang tidak disengaja terkait kapan harus memperbarui variabel penghitung. Oleh karena itu, kita dapat menggeneralisasi hal di atas menggunakan antarmuka fungsional Java.

Pertama, kita harus memikirkan perilaku di dalam loop sebagai konsumen item dalam koleksi dan juga indeks. Ini dapat dimodelkan menggunakan BiConsumer , yang mendefinisikan fungsi terima yang mengambil dua parameter

@FunctionalInterface public interface BiConsumer { void accept(T t, U u); }

Karena bagian dalam perulangan kita adalah sesuatu yang menggunakan dua nilai, kita dapat menulis operasi perulangan umum. Ini bisa mengambil Iterable dari data sumber, di mana untuk setiap loop akan berjalan, dan BiConsumer untuk menjalankan operasi pada setiap item dan indeksnya. Kita dapat membuat ini generik dengan parameter tipe T :

static  void forEachWithCounter(Iterable source, BiConsumer consumer) { int i = 0; for (T item : source) { consumer.accept(i, item); i++; } }

Kita dapat menggunakan ini dengan contoh peringkat film kita dengan menyediakan implementasi untuk BiConsumer sebagai lambda:

List rankings = new ArrayList(); forEachWithCounter(movies, (i, movie) -> { String ranking = (i + 1) + ": " + movies.get(i); rankings.add(ranking); });

4. Menambahkan Penghitung ke forEach dengan Stream

Java Stream API memungkinkan kita untuk mengekspresikan bagaimana data kita melewati filter dan transformasi. Ini juga menyediakan fungsi forEach . Mari kita coba mengubahnya menjadi operasi yang menyertakan penghitung.

Fungsi Stream forEach meminta Konsumen memproses item berikutnya. Namun, kami dapat membuat Konsumen tersebut untuk melacak penghitung dan meneruskan item tersebut ke BiConsumer :

public static  Consumer withCounter(BiConsumer consumer) { AtomicInteger counter = new AtomicInteger(0); return item -> consumer.accept(counter.getAndIncrement(), item); }

Fungsi ini mengembalikan lambda baru. Lambda itu menggunakan objek AtomicInteger untuk melacak penghitung selama iterasi. Fungsi getAndIncrement dipanggil setiap kali ada item baru.

Lambda yang dibuat oleh fungsi ini didelegasikan ke BiConsumer yang diteruskan sehingga algoritme dapat memproses item dan indeksnya.

Mari kita lihat ini digunakan oleh contoh peringkat film kami terhadap Stream yang disebut film :

List rankings = new ArrayList(); movies.forEach(withCounter((i, movie) -> { String ranking = (i + 1) + ": " + movie; rankings.add(ranking); }));

Di dalam forEach adalah panggilan ke fungsi withCounter untuk membuat objek yang melacak hitungan dan bertindak sebagai Konsumen yang juga diteruskan oleh operasi forEach nilainya.

5. Kesimpulan

Dalam artikel singkat ini, kami telah melihat tiga cara untuk memasang penghitung ke Java untuk setiap operasi.

Kami melihat cara melacak indeks item saat ini pada setiap implementasi mereka untuk sebuah loop. Kami kemudian melihat cara menggeneralisasi pola ini dan cara menambahkannya ke operasi streaming.

Seperti biasa, kode contoh untuk artikel ini tersedia di GitHub.