Panduan Pemula untuk Java Streams

Hai sana, para penyihir Java masa depan! Hari ini, kita akan memulai perjalanan menarik ke dunia Java Streams. Jangan khawatir jika Anda baru dalam programming – saya akan menjadi panduan ramah Anda, dan kita akan berjalan langkah demi langkah. Pada akhir panduan ini, Anda akan dapat mengolah data seperti seorang ahli!

Java - Streams

Apa Itu Stream di Java?

Imaginasikan Anda berada di restoran sushi dengan konveyor. Piring sushi terus bergerak melampaui Anda, dan Anda dapat memilih apa yang Anda inginkan. Itu sebenarnya apa yang dimaksudkan oleh Stream di Java – itu adalah sebuah urutan elemen yang Anda dapat proses satu per satu, tanpa perlu menyimpan semuanya di memori sekaligus.

Stream diperkenalkan di Java 8 untuk membuat pekerjaan dengan kumpulan data menjadi mudah dan efisien. Mereka memungkinkan kita untuk melakukan operasi pada data dalam cara yang lebih deklaratif, mengatakan kepada Java apa yang kita inginkan dilakukan, bukan bagaimana untuk melakukannya.

Menghasilkan Streams di Java

Mari kita mulai dengan membuat Stream pertama kita. Ada beberapa cara untuk melakukan ini, tapi kita akan mulai dengan contoh sederhana:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamExample {
public static void main(String[] args) {
// Membuat Stream dari List
List<String> buah = Arrays.asList("apple", "banana", "cherry", "date");
Stream<String> streamBuah = buah.stream();

// Membuat Stream secara langsung
Stream<Integer> streamAngka = Stream.of(1, 2, 3, 4, 5);
}
}

Dalam contoh ini, kita telah membuat dua Streams. Yang pertama, streamBuah, dibuat dari List buah. Yang kedua, streamAngka, dibuat langsung menggunakan metode Stream.of().

Operasi Stream Umum

Sekarang kita memiliki Streams, mari kita lihat beberapa operasi umum yang dapat kita lakukan pada mereka.

Metode forEach

Metode forEach seperti seorang robot ramah yang melewati setiap elemen di Stream dan melakukan sesuatu dengannya. Mari kita gunakan itu untuk mencetak buah-buahan kita:

buah.stream().forEach(buah -> System.out.println(buah));

Ini akan mencetak:

apple
banana
cherry
date

Di sini, buah -> System.out.println(buah) adalah sebuah ekspresi lambda. Itu adalah cara singkat untuk mengatakan kepada Java apa yang harus dilakukan dengan setiap elemen.

Metode map

Metode map seperti tongkat sihir yang mengubah setiap elemen di Stream. Mari kita gunakan itu untuk membuat semua buah-buahan kita menjadi huruf besar:

List<String> buahHurufBesar = buah.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(buahHurufBesar);

Ini akan mencetak:

[APPLE, BANANA, CHERRY, DATE]

String::toUpperCase adalah sebuah referensi metode, fitur menarik lainnya di Java yang seperti mengatakan "gunakan metode toUpperCase pada setiap String".

Metode filter

Metode filter seperti seorang bouncer di klub, hanya membiarkan elemen tertentu melewati. Mari kita gunakan itu untuk hanya menyimpan buah-buahan yang dimulai dengan huruf 'a':

List<String> buahA = buah.stream()
.filter(buah -> buah.startsWith("a"))
.collect(Collectors.toList());
System.out.println(buahA);

Ini akan mencetak:

[apple]

Metode limit

Metode limit seperti mengatakan "Saya hanya ingin ini banyak, terima kasih!" Itu membatasi Stream ke sejumlah elemen tertentu:

List<String> duaBuahPertama = buah.stream()
.limit(2)
.collect(Collectors.toList());
System.out.println(duaBuahPertama);

Ini akan mencetak:

[apple, banana]

Metode sorted

Metode sorted seperti mengatur buku di rak. Itu mengurutkan elemen-elemennya:

List<String> buahTerurut = buah.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(buahTerurut);

Ini akan mencetak:

[apple, banana, cherry, date]

Pemrosesan Paralel

Salah satu hal menarik tentang Streams adalah mereka dapat dengan mudah diproses paralel, secara potensial mempercepat operasi pada dataset besar. Anda dapat membuat stream paralel seperti ini:

buah.parallelStream().forEach(buah -> System.out.println(buah + " " + Thread.currentThread().getName()));

Ini mungkin mencetak sesuatu seperti ini:

banana ForkJoinPool.commonPool-worker-1
apple main
date ForkJoinPool.commonPool-worker-2
cherry ForkJoinPool.commonPool-worker-3

Urutan mungkin berbeda setiap kali Anda menjalankan itu, karena buah-buahan diproses secara paralel!

Collectors

Collectors adalah alat khusus yang membantu kita mengumpulkan hasil operasi Stream. Kita telah menggunakan collect(Collectors.toList()) dalam contoh kita untuk mengubah hasil Stream kembali ke List. Ada banyak Collectors lain yang berguna:

// Menggabungkan elemen menjadi String
String stringBuah = buah.stream().collect(Collectors.joining(", "));
System.out.println(stringBuah);  // Mencetak: apple, banana, cherry, date

// Menghitung elemen
long jumlahBuah = buah.stream().collect(Collectors.counting());
System.out.println(jumlahBuah);  // Mencetak: 4

// Mengelompokkan elemen
Map<Character, List<String>> kelompokBuah = buah.stream()
.collect(Collectors.groupingBy(buah -> buah.charAt(0)));
System.out.println(kelompokBuah);  // Mencetak: {a=[apple], b=[banana], c=[cherry], d=[date]}

Statistik

Streams juga dapat membantu kita menghitung statistik pada data numerik:

List<Integer> angka = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = angka.stream().mapToInt(Integer::intValue).summaryStatistics();
System.out.println("Jumlah: " + stats.getCount());
System.out.println("Jumlah: " + stats.getSum());
System.out.println("Minimum: " + stats.getMin());
System.out.println("Maksimum: " + stats.getMax());
System.out.println("Rata-rata: " + stats.getAverage());

Ini akan mencetak:

Jumlah: 5
Jumlah: 15
Minimum: 1
Maksimum: 5
Rata-rata: 3.0

Contoh Java Streams

Mari kita gabungkan semua itu dengan sebuah contoh yang lebih kompleks. Bayangkan kita memiliki daftar siswa dengan usia dan nilai mereka:

class Student {
String name;
int age;
double grade;

Student(String name, int age, double grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
}

public class StreamExample {
public static void main(String[] args) {
List<Student> siswa = Arrays.asList(
new Student("Alice", 22, 90.5),
new Student("Bob", 20, 85.0),
new Student("Charlie", 21, 92.3),
new Student("David", 23, 88.7)
);

// Mendapatkan nilai rata-rata siswa di atas 21
double nilaiRataRata = siswa.stream()
.filter(siswa -> siswa.age > 21)
.mapToDouble(siswa -> siswa.grade)
.average()
.orElse(0.0);

System.out.println("Nilai rata-rata siswa di atas 21: " + nilaiRataRata);

// Mendapatkan nama siswa terbaik 2 berdasarkan nilai
List<String> siswaTerbaik = siswa.stream()
.sorted((s1, s2) -> Double.compare(s2.grade, s1.grade))
.limit(2)
.map(siswa -> siswa.name)
.collect(Collectors.toList());

System.out.println("2 siswa terbaik: " + siswaTerbaik);
}
}

Contoh ini menunjukkan bagaimana kita dapat merencanakan beberapa operasi Stream bersamaan untuk melakukan tugas pengolahan data kompleks dalam cara yang singkat dan dapat dibaca.

Dan itu saja, teman-teman! Anda telah mengambil langkah pertama ke dunia Java Streams. Ingat, latihan membuat sempurna, jadi jangan khawatir untuk mencoba konsep-konsep ini. Sebelum Anda tahu, Anda akan dapat mengolah data seperti seorang ahli! Selamat coding!

Credits: Image by storyset