Python

🌊 Java Streams API: Panduan Lengkap

Tutorial lengkap Java Streams API β€” stream operations, filter, map, reduce, collectors, parallel streams, dan quiz interaktif dengan contoh kode praktis

1. Pengenalan Streams API

Java Streams API diperkenalkan di Java 8 dan memungkinkan kita memproses data secara deklaratif dan functional. Alih-alih menulis loop manual, kita mendeskripsikan apa yang ingin dilakukan terhadap data menggunakan pipeline operasi.

Stream bukan collection β€” Stream adalah representasi lazy dari sequence elemen yang mendukung operasi aggregate. Stream tidak mengubah data sumber (non-mutating) dan bisa hanya digunakan sekali (single-use).

Mengapa Menggunakan Streams?

Keunggulan Penjelasan
Kode Lebih RingkasOperasi kompleks dalam satu baris pipeline
DeclarativeFokus pada "apa" bukan "bagaimana"
ComposableBisa dirangkai (chaining) menjadi pipeline
Lazy EvaluationOperasi intermediate tidak dijalankan sampai terminal
ParallelizableMudah dijalankan secara paralel dengan parallel stream
Type-SafeCompile-time type checking dengan Generics

Stream vs Loop Tradisional

Java β€” Imperative vs Declarative
import java.util.*;
import java.util.stream.*;

public class PerbandinganStream {
    public static void main(String[] args) {
        List<String> nama = List.of("Budi", "Ani", "Citra", "Dina", "Eko", "Fajar");

        // ===== LOOP TRADISIONAL (Imperative) =====
        List<String> hasil1 = new ArrayList<>();
        for (String n : nama) {
            if (n.length() > 3) {           // filter
                hasil1.add(n.toUpperCase()); // transform
            }
        }
        Collections.sort(hasil1);            // sort
        System.out.println("Loop: " + hasil1);

        // ===== STREAM (Declarative) =====
        List<String> hasil2 = nama.stream()
            .filter(n -> n.length() > 3)     // filter
            .map(String::toUpperCase)         // transform
            .sorted()                         // sort
            .collect(Collectors.toList());    // collect
        System.out.println("Stream: " + hasil2);

        // Keduanya menghasilkan: [BUDI, CITRA, DINA, FAJAR]
    }
}
Diagram: Stream Pipeline
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  STREAM PIPELINE                          β”‚
β”‚                                                          β”‚
β”‚  Source ──▢ Intermediate ──▢ Intermediate ──▢ Terminal   β”‚
β”‚  (data)    Operations        Operations       Operation  β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚List │──▢│ .filter()│──▢│  .map()  │──▢│.collect() β”‚ β”‚
β”‚  β”‚.strmβ”‚   β”‚  .sorted()β”‚   β”‚  .limit()β”‚   β”‚ .reduce() β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚ .forEach()β”‚ β”‚
β”‚                                           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚  Intermediate: mengembalikan Stream baru (lazy)          β”‚
β”‚  Terminal: menghasilkan hasil akhir (trigger execution)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Membuat Stream

Ada banyak cara untuk membuat Stream di Java. Berikut yang paling umum:

Java β€” Membuat Stream
import java.util.*;
import java.util.stream.*;

public class MembuatStream {
    public static void main(String[] args) {
        // 1. Dari Collection
        List<String> buah = List.of("Apel", "Mangga", "Jeruk");
        Stream<String> stream1 = buah.stream();

        Set<Integer> angkaSet = Set.of(1, 2, 3, 4, 5);
        Stream<Integer> stream2 = angkaSet.stream();

        // 2. Dari Array
        String[] array = {"Java", "Python", "Go"};
        Stream<String> stream3 = Arrays.stream(array);

        // 3. Dari Array β€” sebagian (range)
        int[] numbers = {10, 20, 30, 40, 50};
        IntStream stream4 = Arrays.stream(numbers, 1, 4); // index 1 s/d 3

        // 4. Stream.of() β€” langsung dari nilai
        Stream<String> stream5 = Stream.of("A", "B", "C", "D");
        Stream<Integer> stream6 = Stream.of(1, 2, 3);

        // 5. Stream.generate() β€” infinite stream
        Stream<Double> random = Stream.generate(Math::random).limit(5);
        random.forEach(System.out::println);

        // 6. Stream.iterate() β€” infinite stream dengan pola
        Stream<Integer> genap = Stream.iterate(0, n -> n + 2).limit(10);
        genap.forEach(n -> System.out.print(n + " ")); // 0 2 4 6 8 10 12 14 16 18
        System.out.println();

        // 7. IntStream.range() dan rangeClosed()
        IntStream.range(1, 6).forEach(n -> System.out.print(n + " "));     // 1 2 3 4 5
        System.out.println();
        IntStream.rangeClosed(1, 6).forEach(n -> System.out.print(n + " ")); // 1 2 3 4 5 6
        System.out.println();

        // 8. Stream dari String (karakter)
        IntStream chars = "Hello".chars();
        chars.forEach(c -> System.out.print((char) c + " ")); // H e l l o
        System.out.println();

        // 9. Stream.concat() β€” menggabung dua stream
        Stream<String> s1 = Stream.of("A", "B");
        Stream<String> s2 = Stream.of("C", "D");
        Stream<String> combined = Stream.concat(s1, s2);
        combined.forEach(System.out::print); // ABCD
        System.out.println();

        // 10. Stream.empty() β€” stream kosong
        Stream<String> empty = Stream.empty();
        System.out.println("Empty count: " + empty.count()); // 0
    }
}
πŸ’‘ Tips

Stream hanya bisa digunakan satu kali. Jika kamu mencoba mengoperasikan Stream yang sudah pernah dipakai terminal operation, kamu akan mendapat IllegalStateException: stream has already been operated upon or closed. Buat Stream baru jika perlu memproses ulang data yang sama.

3. Filter dan Map

filter menyaring elemen berdasarkan kondisi, map mengubah setiap elemen menjadi bentuk lain. Keduanya adalah intermediate operation yang paling sering digunakan.

Java β€” Filter & Map
import java.util.*;
import java.util.stream.*;

public class FilterMapDemo {
    public static void main(String[] args) {
        List<Integer> angka = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // ===== FILTER β€” saring elemen =====
        // Ambil hanya angka genap
        List<Integer> genap = angka.stream()
            .filter(n -> n % 2 == 0)
            .collect(Collectors.toList());
        System.out.println("Genap: " + genap); // [2, 4, 6, 8, 10]

        // Ambil angka > 5
        List<Integer> besar = angka.stream()
            .filter(n -> n > 5)
            .collect(Collectors.toList());
        System.out.println("> 5: " + besar); // [6, 7, 8, 9, 10]

        // ===== MAP β€” transformasi elemen =====
        // Kuadratkan semua angka
        List<Integer> kuadrat = angka.stream()
            .map(n -> n * n)
            .collect(Collectors.toList());
        System.out.println("Kuadrat: " + kuadrat);
        // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

        // ===== COMBINE filter + map =====
        List<String> nama = List.of("Budi", "Ani", "Citra", "Dina", "Eko");

        // Filter panjang > 3, ubah ke uppercase, tambah prefix
        List<String> hasil = nama.stream()
            .filter(n -> n.length() > 3)
            .map(String::toUpperCase)
            .map(n -> "Hi, " + n + "!")
            .collect(Collectors.toList());
        System.out.println("Sapaan: " + hasil);
        // [Hi, BUDI!, Hi, CITRA!, Hi, DINA!, Hi, FAJAR!]

        // ===== FLATMAP β€” flatten nested streams =====
        List<List<Integer>> nested = List.of(
            List.of(1, 2, 3),
            List.of(4, 5),
            List.of(6, 7, 8, 9)
        );

        // Tanpa flatmap β€” hanya bisa stream per list
        // Dengan flatmap β€” gabungkan semua jadi satu stream
        List<Integer> flat = nested.stream()
            .flatMap(Collection::stream)
            .collect(Collectors.toList());
        System.out.println("Flat: " + flat); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

        // ===== DISTINCT β€” hapus duplikat =====
        List<String> warna = List.of("Merah", "Biru", "Merah", "Hijau", "Biru");
        List<String> unik = warna.stream()
            .distinct()
            .collect(Collectors.toList());
        System.out.println("Unik: " + unik); // [Merah, Biru, Hijau]

        // ===== SORTED =====
        List<Integer> acak = List.of(5, 2, 8, 1, 9, 3);
        List<Integer> urut = acak.stream()
            .sorted()
            .collect(Collectors.toList());
        System.out.println("Urut: " + urut); // [1, 2, 3, 5, 8, 9]

        // ===== LIMIT & SKIP =====
        List<Integer> pertama3 = angka.stream()
            .limit(3)
            .collect(Collectors.toList());
        System.out.println("3 pertama: " + pertama3); // [1, 2, 3]

        List<Integer> skip5 = angka.stream()
            .skip(5)
            .collect(Collectors.toList());
        System.out.println("Skip 5: " + skip5); // [6, 7, 8, 9, 10]

        // ===== PEEK β€” debug/observe tanpa mengubah =====
        List<Integer> peekDemo = angka.stream()
            .filter(n -> n % 2 == 0)
            .peek(n -> System.out.println("Filtered: " + n))
            .map(n -> n * 10)
            .peek(n -> System.out.println("Mapped: " + n))
            .collect(Collectors.toList());
        System.out.println("Peek result: " + peekDemo);
    }
}

4. Reduce dan Akumulasi

reduce adalah terminal operation yang mengkombinasikan semua elemen stream menjadi satu nilai hasil. Ini disebut aggregation atau fold dalam functional programming.

Java β€” Reduce
import java.util.*;
import java.util.stream.*;

public class ReduceDemo {
    public static void main(String[] args) {
        List<Integer> angka = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // ===== SUM β€” Jumlah total =====
        int total = angka.stream()
            .reduce(0, Integer::sum);
        System.out.println("Total: " + total); // 55

        // Atau gunakan IntStream
        int totalAlt = angka.stream()
            .mapToInt(Integer::intValue)
            .sum();
        System.out.println("Total (alt): " + totalAlt); // 55

        // ===== PRODUCT β€” Perkalian semua =====
        int product = angka.stream()
            .reduce(1, (a, b) -> a * b);
        System.out.println("Product: " + product); // 3628800

        // ===== MAX / MIN =====
        Optional<Integer> max = angka.stream()
            .reduce(Integer::max);
        max.ifPresent(m -> System.out.println("Max: " + m)); // 10

        Optional<Integer> min = angka.stream()
            .reduce(Integer::min);
        min.ifPresent(m -> System.out.println("Min: " + m)); // 1

        // ===== CONCATENATE strings =====
        List<String> kata = List.of("Java", "Streams", "API");
        String gabungan = kata.stream()
            .reduce("", (a, b) -> a.isEmpty() ? b : a + " " + b);
        System.out.println("Gabungan: " + gabungan); // "Java Streams API"

        // Atau lebih mudah dengan joining
        String joinSimple = kata.stream()
            .collect(Collectors.joining(" "));
        System.out.println("Joining: " + joinSimple);

        // ===== COUNT =====
        long count = angka.stream()
            .filter(n -> n > 5)
            .count();
        System.out.println("Count > 5: " + count); // 5

        // ===== STATISTICS =====
        IntSummaryStatistics stats = angka.stream()
            .mapToInt(Integer::intValue)
            .summaryStatistics();
        System.out.println("Count: " + stats.getCount());
        System.out.println("Sum: " + stats.getSum());
        System.out.println("Min: " + stats.getMin());
        System.out.println("Max: " + stats.getMax());
        System.out.println("Average: " + stats.getAverage());
        // Count: 10, Sum: 55, Min: 1, Max: 10, Average: 5.5

        // ===== MATCHING β€” anyMatch, allMatch, noneMatch =====
        boolean adaGenap = angka.stream().anyMatch(n -> n % 2 == 0);
        boolean semuaPositif = angka.stream().allMatch(n -> n > 0);
        boolean tidakAdaNol = angka.stream().noneMatch(n -> n == 0);

        System.out.println("Ada genap? " + adaGenap);         // true
        System.out.println("Semua positif? " + semuaPositif); // true
        System.out.println("Tidak ada nol? " + tidakAdaNol);  // true

        // ===== FIND β€” findFirst, findAny =====
        Optional<Integer> pertama = angka.stream()
            .filter(n -> n > 5)
            .findFirst();
        pertama.ifPresent(n -> System.out.println("Pertama > 5: " + n)); // 6

        // Reduce dengan 3 argumen (untuk parallel stream)
        int totalParallel = angka.parallelStream()
            .reduce(0, Integer::sum, Integer::sum);
        System.out.println("Parallel sum: " + totalParallel);
    }
}

5. Collectors β€” Terminal Operations

Collectors adalah class utility yang menyediakan berbagai method untuk mengumpulkan elemen stream menjadi collection, string, map, atau nilai agregat.

Java β€” Collectors
import java.util.*;
import java.util.stream.*;

public class CollectorsDemo {

    // Class data untuk contoh
    record Mahasiswa(String nama, String jurusan, double ipk) {}

    public static void main(String[] args) {
        List<Mahasiswa> mhs = List.of(
            new Mahasiswa("Budi", "TI", 3.50),
            new Mahasiswa("Ani", "SI", 3.89),
            new Mahasiswa("Citra", "TI", 3.75),
            new Mahasiswa("Dina", "SI", 3.92),
            new Mahasiswa("Eko", "TI", 3.20),
            new Mahasiswa("Fajar", "SI", 3.65)
        );

        // ===== toList() =====
        List<String> namaList = mhs.stream()
            .map(Mahasiswa::nama)
            .collect(Collectors.toList());
        System.out.println("Nama: " + namaList);

        // ===== toSet() β€” hapus duplikat =====
        Set<String> jurusan = mhs.stream()
            .map(Mahasiswa::jurusan)
            .collect(Collectors.toSet());
        System.out.println("Jurusan: " + jurusan); // [TI, SI]

        // ===== toMap() =====
        Map<String, Double> namaIpk = mhs.stream()
            .collect(Collectors.toMap(Mahasiswa::nama, Mahasiswa::ipk));
        System.out.println("Map: " + namaIpk);

        // ===== joining() β€” gabung String =====
        String semuaNama = mhs.stream()
            .map(Mahasiswa::nama)
            .collect(Collectors.joining(", "));
        System.out.println("Joining: " + semuaNama);
        // "Budi, Ani, Citra, Dina, Eko, Fajar"

        String denganPrefix = mhs.stream()
            .map(Mahasiswa::nama)
            .collect(Collectors.joining(", ", "[", "]"));
        System.out.println("Joining: " + denganPrefix); // [Budi, Ani, ...]

        // ===== counting() =====
        long jumlah = mhs.stream()
            .filter(m -> m.ipk() > 3.5)
            .collect(Collectors.counting());
        System.out.println("IPK > 3.5: " + jumlah);

        // ===== summingDouble / averagingDouble =====
        double totalIpk = mhs.stream()
            .collect(Collectors.summingDouble(Mahasiswa::ipk));
        System.out.println("Total IPK: " + totalIpk);

        double rataIpk = mhs.stream()
            .collect(Collectors.averagingDouble(Mahasiswa::ipk));
        System.out.println("Rata-rata IPK: " + rataIpk);

        // ===== groupingBy() β€” kelompokkan data =====
        Map<String, List<Mahasiswa>> perJurusan = mhs.stream()
            .collect(Collectors.groupingBy(Mahasiswa::jurusan));
        System.out.println("\nPer Jurusan:");
        perJurusan.forEach((j, list) -> {
            System.out.println("  " + j + ": " + list.stream()
                .map(Mahasiswa::nama).collect(Collectors.joining(", ")));
        });

        // Grouping dengan counting
        Map<String, Long> countJurusan = mhs.stream()
            .collect(Collectors.groupingBy(Mahasiswa::jurusan, Collectors.counting()));
        System.out.println("Count per jurusan: " + countJurusan); // {TI=3, SI=3}

        // Grouping dengan averaging
        Map<String, Double> avgJurusan = mhs.stream()
            .collect(Collectors.groupingBy(Mahasiswa::jurusan,
                Collectors.averagingDouble(Mahasiswa::ipk)));
        System.out.println("Avg IPK per jurusan: " + avgJurusan);

        // ===== partitioningBy() β€” bagi jadi true/false =====
        Map<Boolean, List<Mahasiswa>> partisi = mhs.stream()
            .collect(Collectors.partitioningBy(m -> m.ipk() >= 3.7));
        System.out.println("\nIPK >= 3.7: " + partisi.get(true).stream()
            .map(Mahasiswa::nama).collect(Collectors.joining(", ")));
        System.out.println("IPK < 3.7: " + partisi.get(false).stream()
            .map(Mahasiswa::nama).collect(Collectors.joining(", ")));

        // ===== maxBy / minBy =====
        mhs.stream()
            .collect(Collectors.maxBy(Comparator.comparingDouble(Mahasiswa::ipk)))
            .ifPresent(m -> System.out.println("\nIPK tertinggi: " + m.nama() + " (" + m.ipk() + ")"));
    }
}

6. Advanced Operations

Java β€” Advanced Streams
import java.util.*;
import java.util.stream.*;

public class AdvancedStreamDemo {

    record Produk(String nama, String kategori, double harga) {}

    public static void main(String[] args) {
        List<Produk> produk = List.of(
            new Produk("Laptop", "Elektronik", 12000000),
            new Produk("Mouse", "Elektronik", 150000),
            new Produk("Baju", "Fashion", 250000),
            new Produk("Celana", "Fashion", 300000),
            new Produk("HP", "Elektronik", 5000000),
            new Produk("Sepatu", "Fashion", 450000),
            new Produk("Tablet", "Elektronik", 3500000)
        );

        // ===== Pipeline Kompleks =====
        // Cari 3 produk Elektronik termurah, format harganya
        System.out.println("=== 3 Elektronik Termurah ===");
        produk.stream()
            .filter(p -> p.kategori().equals("Elektronik"))
            .sorted(Comparator.comparingDouble(Produk::harga))
            .limit(3)
            .map(p -> String.format("%s: Rp %,.0f", p.nama(), p.harga()))
            .forEach(System.out::println);

        // ===== toMap dengan merge function =====
        // Jika ada key duplikat, pilih yang harga lebih tinggi
        Map<String, Double> maxPerKategori = produk.stream()
            .collect(Collectors.toMap(
                Produk::kategori,
                Produk::harga,
                Math::max  // merge function
            ));
        System.out.println("\nMax per kategori: " + maxPerKategori);

        // ===== Custom Collector dengan teeing =====
        // (Java 12+)
        // Gabungkan 2 collector sekaligus
        // Contoh: hitung total dan rata-rata sekaligus

        // ===== Map operations pada Stream =====
        Map<String, List<Produk>> byKategori = produk.stream()
            .collect(Collectors.groupingBy(Produk::kategori));

        // Stream dari Map entrySet
        System.out.println("\n=== Kategori Analysis ===");
        byKategori.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .forEach(entry -> {
                double avg = entry.getValue().stream()
                    .mapToDouble(Produk::harga)
                    .average().orElse(0);
                System.out.printf("%s: %d produk, rata-rata Rp %,.0f%n",
                    entry.getKey(), entry.getValue().size(), avg);
            });

        // ===== Method reference berbagai bentuk =====
        List<String> namaList = List.of("java", "PYTHON", "Go");

        // Static method reference
        List<String> upper = namaList.stream()
            .map(String::toUpperCase)        // s -> s.toUpperCase()
            .collect(Collectors.toList());

        // Instance method reference pada parameter
        List<Integer> lengths = namaList.stream()
            .map(String::length)             // s -> s.length()
            .collect(Collectors.toList());

        // Constructor reference
        List<StringBuilder> builders = namaList.stream()
            .map(StringBuilder::new)         // s -> new StringBuilder(s)
            .collect(Collectors.toList());

        System.out.println("\nUpper: " + upper);
        System.out.println("Lengths: " + lengths);

        // ===== toArray() =====
        String[] namaArray = produk.stream()
            .map(Produk::nama)
            .toArray(String[]::new);
        System.out.println("Array: " + Arrays.toString(namaArray));

        // ===== reduce dengan identity =====
        String csv = namaList.stream()
            .reduce("", (a, b) -> a.isEmpty() ? b : a + ", " + b);
        System.out.println("CSV: " + csv);
    }
}

7. Parallel Streams

Parallel stream memproses elemen secara paralel menggunakan Fork/Join framework. Ini sangat berguna untuk dataset besar di mana operasi bisa dilakukan secara independen per elemen.

Java β€” Parallel Streams
import java.util.*;
import java.util.stream.*;

public class ParallelStreamDemo {
    public static void main(String[] args) {
        // Membuat parallel stream
        List<Integer> angka = IntStream.rangeClosed(1, 10000000)
            .boxed()
            .collect(Collectors.toList());

        // ===== SEQUENTIAL =====
        long start1 = System.currentTimeMillis();
        long sum1 = angka.stream()
            .mapToLong(Integer::longValue)
            .sum();
        long time1 = System.currentTimeMillis() - start1;
        System.out.println("Sequential sum: " + sum1 + " (" + time1 + "ms)");

        // ===== PARALLEL =====
        long start2 = System.currentTimeMillis();
        long sum2 = angka.parallelStream()
            .mapToLong(Integer::longValue)
            .sum();
        long time2 = System.currentTimeMillis() - start2;
        System.out.println("Parallel sum: " + sum2 + " (" + time2 + "ms)");

        // ===== Contoh filter paralel =====
        List<String> data = IntStream.rangeClosed(1, 100)
            .mapToObj(i -> "Data-" + i)
            .collect(Collectors.toList());

        // Filter paralel
        List<String> filtered = data.parallelStream()
            .filter(s -> {
                // Simulasi operasi berat per item
                try { Thread.sleep(1); } catch (InterruptedException e) {}
                return s.hashCode() % 2 == 0;
            })
            .collect(Collectors.toList());
        System.out.println("Filtered (parallel): " + filtered.size() + " items");

        // ===== Peringatan: Pastikan operasi THREAD-SAFE =====
        // ❌ JANGAN: mutable shared state
        // List<Integer> hasil = new ArrayList<>();
        // angka.parallelStream().filter(n -> n > 5).forEach(hasil::add); // UNSAFE!

        // βœ… BENAR: gunakan collector
        List<Integer> hasilAman = angka.parallelStream()
            .filter(n -> n > 9999990)
            .collect(Collectors.toList()); // collector sudah thread-safe
        System.out.println("Aman: " + hasilAman);

        // ===== Kapan menggunakan parallel? =====
        // βœ… Dataset BESAR (jutaan elemen)
        // βœ… Operasi INDEPENDEN per elemen
        // βœ… Operasi CUKUP BERAT (bukan sekedar add/multiply)
        // βœ… Tidak ada side effects (no mutation)
        // ❌ Dataset kecil β€” overhead thread lebih mahal
        // ❌ Operasi bergantung urutan (ordered)
        // ❌ I/O bound operations

        // ===== Mengatur parallelism =====
        // System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "4");
    }
}
πŸ’‘ Tips

Jangan otomatis menggunakan parallel stream untuk semua operasi. Untuk dataset kecil (<10.000 elemen), sequential stream seringkali lebih cepat karena tidak ada overhead manajemen thread. Gunakan parallel hanya untuk dataset besar dengan operasi yang benar-benar independen dan berat. Selalu benchmark!

8. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Java Streams API:

Pertanyaan 1: Apa perbedaan antara intermediate dan terminal operation pada Stream?

a) Intermediate menghasilkan akhir, terminal mengembalikan Stream baru
b) Intermediate mengembalikan Stream baru (lazy), terminal menghasilkan hasil akhir (trigger eksekusi)
c) Tidak ada perbedaan
d) Intermediate hanya untuk List, terminal untuk Set

Pertanyaan 2: Method apa yang digunakan untuk menggabungkan semua elemen String dalam Stream?

a) Collectors.joining()
b) Stream.concat()
c) Stream.merge()
d) Collectors.toList()

Pertanyaan 3: Apa fungsi dari flatMap() dibanding map()?

a) flatMap lebih cepat dari map
b) flatMap menggabungkan (flatten) nested stream menjadi satu stream datar
c) flatMap hanya untuk tipe Integer
d) flatMap mengurutkan elemen

Pertanyaan 4: Apa yang terjadi jika menggunakan Stream yang sudah dilakukan terminal operation?

a) Stream mengulang dari awal secara otomatis
b) Mengembalikan null
c) Menghasilkan IllegalStateException
d) Mengembalikan Stream kosong

Pertanyaan 5: Kapan sebaiknya menggunakan parallel stream?

a) Selalu, karena lebih cepat
b) Hanya untuk dataset kecil (<100 elemen)
c) Untuk dataset besar dengan operasi independen dan berat
d) Hanya untuk operasi yang membutuhkan urutan
πŸ” Zoom
100%
🎨 Tema