Cara Mengakses Kaunter Pengulangan di Setiap Gelung

1. Gambaran keseluruhan

Sambil mengulangi data di Java, kami mungkin ingin mengakses item saat ini dan posisinya di sumber data.

Ini sangat mudah dicapai dalam klasik untuk gelung, di mana kedudukannya biasanya menjadi tumpuan pengiraan gelung, tetapi memerlukan sedikit lebih banyak kerja ketika kita menggunakan konstruk seperti untuk setiap gelung atau aliran.

Dalam tutorial ringkas ini, kita akan melihat beberapa cara untuk setiap operasi merangkumi pembilang.

2. Melaksanakan Pembilang

Mari kita mulakan dengan contoh mudah. Kami akan mengambil senarai filem yang dipesan dan mengeluarkannya mengikut peringkatnya.

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

2.1. untuk Gelung

A untuk gelung menggunakan pembilang untuk merujuk item semasa, jadi ini adalah cara mudah untuk mengendalikan data dan indeksnya dalam senarai:

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

Oleh kerana Senarai ini mungkin ArrayList , operasi mendapatkannya cekap, dan kod di atas adalah penyelesaian mudah 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 diulang melalui cara ini. Kadang kala get adalah operasi yang memerlukan waktu, atau kita hanya dapat memproses elemen sumber data seterusnya menggunakan Stream atau Iterable.

2.2. untuk Setiap Gelung

Kami akan terus menggunakan senarai filem kami, tetapi mari kita berpura-pura bahawa kita hanya dapat berulang menggunakan Java untuk setiap konstruk:

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

Di sini kita perlu menggunakan pemboleh ubah berasingan untuk mengesan indeks semasa. Kita boleh membuatnya di luar gelung, dan menambahnya di dalam:

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

Kita harus perhatikan bahawa kita harus meningkatkan penghitung setelah digunakan dalam lingkaran.

3. Berfungsi untuk Setiap

Menulis peluasan penghitung setiap kali kita memerlukannya boleh menyebabkan penduaan kod dan mungkin berisiko mengalami bug yang tidak disengajakan mengenai kapan mengemas kini pemboleh ubah pembilang. Oleh itu, kita dapat menggeneralisasikan perkara di atas menggunakan antara muka fungsional Java.

Pertama, kita harus memikirkan tingkah laku di dalam gelung sebagai pengguna kedua-dua item dalam koleksi dan juga indeks. Ini dapat dimodelkan menggunakan BiConsumer , yang menentukan fungsi penerimaan yang mengambil dua parameter

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

Oleh kerana bahagian dalam gelung kami adalah sesuatu yang menggunakan dua nilai, kami dapat menulis operasi gelung umum. Ini dapat mengambil Iterable dari data sumber, di mana untuk setiap gelung akan dijalankan, dan BiConsumer agar operasi dapat dilakukan pada setiap item dan indeksnya. Kita boleh menjadikannya generik dengan parameter jenis T :

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

Kami dapat menggunakannya dengan contoh peringkat filem kami dengan menyediakan pelaksanaan untuk BiConsumer sebagai lambda:

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

4. Menambah Pembilang untuk forEach dengan Aliran

Java Stream API memungkinkan kita untuk menyatakan bagaimana data kita melalui penapis dan transformasi. Ia juga menyediakan fungsi forEach . Mari cuba mengubahnya menjadi operasi yang merangkumi pembilang.

Fungsi Stream forEach mengambil Pelanggan untuk memproses item seterusnya. Namun, kami dapat membuat Pelanggan tersebut untuk mengawasi kaunter 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 mengesan pembilang semasa lelaran. The getAndIncrement fungsi dipanggil setiap kali ada item baru.

Lambda yang dibuat oleh fungsi ini memberi perwakilan kepada BiConsumer yang diserahkan agar algoritma dapat memproses item dan indeksnya.

Mari kita lihat ini digunakan oleh contoh pemeringkatan filem kita berbanding Stream yang disebut filem :

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 sama -sama melacak kiraan dan bertindak sebagai Pengguna bahawa operasi forEach juga melalu nilainya.

5. Kesimpulan

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

Kami melihat bagaimana untuk mengesan indeks item semasa pada setiap pelaksanaannya untuk satu gelung. Kami kemudian melihat bagaimana menggeneralisasikan corak ini dan bagaimana menambahkannya ke operasi streaming.

Seperti biasa kod contoh untuk artikel ini terdapat di GitHub.