All Articles

Pengenalan Java 8 Stream

Java 8 memang udah rilis sejak kapan tahun, tp jujur sejak kerja dikantor yang sekarang (pake .NET) saya udah ga pernah lagi update pengetahuan di Java. Java 8 memiliki beberapa fitur baru, seperti Stream, Collector, Optional, Lambda, dll.

Kali ini saya akan membahas mengenai Stream. Banyak yang membandingkan Stream dengan LINQ nya .NET. Secara konsep tujuannya sama, yaitu memudahkan operasi terhadap objek Collection, anggap seperti kita sedang melakukan query SQL terhadap table database.

Kenapa butuh Stream

Java memang sudah memiliki Collection API yang memiliki segudang kemampuan untuk melakukan operasi terhadap objek Collection. Namu Collection API tidak menyediakan API higher level yang memudahkan melakukan query data, sehingga kita harus menulis cukup banyak baris kode hanya untuk melakukan operasi yang simple. Kemudian, Collection API tidak mendukung pemrosesan data secara parallel, diserahkan sepenuhnya ke developer yang membuat untuk menangani pemrosesan data yang efektif dan efisien secara parallel. Stream menjawab kekurangan itu, dengan membuat API yang simple, to the point dan memaksimalkan fitur parallel data processing.

Mari kita lihat perbedaan Stream dan Collection API dengan contoh kasus berikut : Dari data karyawan yang diberikan, ambil data karyawan tetap lalu tampilkan nama dan diurutkan berdasarkan nama nya ?

public class Example1_Collection {

    public static void main(String[] args) {
        List karyawanList = getAllKaryawan();

        List karyawanTetapList = new ArrayList<>();
        for (Karyawan karyawan : karyawanList) {
            if (karyawan.getType() == TipeKaryawan.KARYAWAN_TETAP) {
                karyawanTetapList.add(karyawan);
            }
        }
        Collections.sort(karyawanTetapList, new Comparator() {
            @Override
            public int compare(Karyawan t1, Karyawan t2) {
                return t1.getNama().length() - t2.getNama().length();
            }
        });
        for (Karyawan karyawanTetap : karyawanTetapList) {
            System.out.println(karyawanTetap.getNama());
        }
    }
}

Dengan Collection API setiap harinya developer java harus menulis 15 baris kode hanya untuk operasi sesimple itu.

Jadi terkadang bukan bagaimana cara menyelesaikannya yg jadi masalah, tapi kemalasan harus nulis segitu baris itu lo yang jadi masalah haha. Yang diinginkan cukup simple, filter data karyawan, urutkan berdasarkan nama dan tampilkan hasilnya itu saja.

Mencoba Stream

Mari kita coba menggunakan Stream untuk mengetahui perbedaannya dengan Collection API

public class Example1_Stream {

    public static void main(String[] args) {
        List karyawanList = getAllKaryawan();

        List karyawanTetapList = karyawanList.stream()
                .filter(karyawan -> karyawan.getType() == TipeKaryawan.KARYAWAN_TETAP)
                .sorted((t1, t2) -> t1.getNama().length() - t2.getNama().length())
                .map(Karyawan::getNama)
                .collect(Collectors.toList());

        karyawanTetapList.forEach(System.out::println);
    }
}

Berikut penjelasan mengenai penggunaan Stream diatas :

  • filter: menyeleksi data dengan kriteria tertentu, semacam WHERE clause
  • sorted: mengurutkan data dengan kriteria tertentu, semacam Order by
  • map: mengambil data yang dinginkan, semacam SELECT nama
  • collect: mengeksekusi Stream dan merubah datanya sebagai List. Stream belum akan dieksekusi sebelum ada istilahnya java operasi terminal seperti: collect, foreach, count, dll

Parallel Streams

Kelebihan lain dari Stream seperti yang telah saya sebutkan diatas adalah kemampuannya menangani proses parallel. Anda hanya perlu memanggil method parallel, maka secara otomatis Stream akan membuat thread sebanyak jumlah CPU yang ada pada komputer anda.

Contoh berikut akan memproses data List berisi angka 1 s/d 100 Stream secara paralel dan datanya akan dibagi kedalam beberapa Thread (group by Thread name)

public class Example1_ParallelStream {

    public static void main(String[] args) {
        Map> numRange = IntStream.rangeClosed(1, 100)
                .parallel()
                .boxed()
                .collect(groupingBy(i -> Thread.currentThread().getName()));

        numRange.forEach((k, v) -> System.out.println(String.format("%s >> %s", k, v)));
    }
}

Contoh diatas akan menghasilkan output seperti ini :

ForkJoinPool.commonPool-worker-7 >> [38, 39, 40, 41, 42, 43, 54, 55, 56, 97, 98, 99, 100]
ForkJoinPool.commonPool-worker-1 >> [19, 20, 21, 22, 23, 24, 25, 51, 52, 53, 88, 89, 90, 91, 92, 93]
ForkJoinPool.commonPool-worker-2 >> [26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 72, 73]
main >> [66, 67, 68]
ForkJoinPool.commonPool-worker-5 >> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 74, 75, 76, 77, 78, 94, 95, 96]
ForkJoinPool.commonPool-worker-6 >> [13, 14, 15, 44, 45, 46, 49, 50, 63, 64, 65, 69, 70, 71, 79, 80, 81]
ForkJoinPool.commonPool-worker-3 >> [47, 48, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, 86, 87]
ForkJoinPool.commonPool-worker-4 >> [16, 17, 18]

Terlihat hasilnya data diproses dan dibagi ke dalam 8 Thread (sesuai jumlah CPU). Output yang tampil bisa berbeda saat anda mencoba, karena proses pembagian data ke masing-masing Thread dilakukan secara otomatis dibelakang layar, percaya aja udah yang paling efektif dan efisien hehe :)

Sekian semoga bermanfaat.