Eğitim Portalı/Java/Stream API
Java01-java/12-stream-api

Stream API

Bir koleksiyondaki verileri işlerken genelde aynı şeyleri yaparız: filtrele, dönüştür, sırala, topla. Geleneksel for döngüleriyle bu işler hem uzar hem de "ne yapıldığı" "nasıl yapıldığı"nın içinde kaybolur. Stream AP…

Stream API

Bir koleksiyondaki verileri işlerken genelde aynı şeyleri yaparız: filtrele, dönüştür, sırala, topla. Geleneksel for döngüleriyle bu işler hem uzar hem de "ne yapıldığı" "nasıl yapıldığı"nın içinde kaybolur. Stream API (Java 8+), veriyi bir akış olarak düşünüp bu işlemleri okunabilir, zincirleme bir biçimde ifade etmemizi sağlar. Lambda'larla birleşince Java'da veri işlemenin modern yüzü ortaya çıkar.

Stream nedir?

Stream, bir veri kaynağı (koleksiyon, dizi, üreteç) üzerinden geçen bir eleman akışıdır. Veriyi saklamaz; üzerinde işlemler tanımlarsın. Bir pipeline üç parçadan oluşur:

  1. Kaynak: list.stream(), Arrays.stream(...), IntStream.range(...)
  2. Ara işlemler (intermediate): filter, map, sorted, distinct, limit — yeni bir stream döndürür, tembeldir (lazy).
  3. Sonlandırıcı (terminal): collect, forEach, count, reduce, sum — akışı tüketir ve sonucu üretir.
List<String> sonuc = isimler.stream()
        .filter(ad -> ad.startsWith("a"))
        .map(String::toUpperCase)
        .sorted()
        .collect(Collectors.toList());

Ara işlemler ancak bir sonlandırıcı çağrıldığında çalışır; buna tembel değerlendirme denir ve gereksiz işlemden kaçınmayı sağlar. Örnek 1 (./Ornek1.java) temel pipeline'ı, forEach, count, allMatch, distinct ve limit'i gösterir.

reduce ve ilkel akışlar

reduce, bir akışı tek bir değere indirger (toplam, çarpım, en büyük...):

int toplam = sayilar.stream().reduce(0, Integer::sum);

Sayısal işlemlerde IntStream / LongStream / DoubleStream kullanmak daha verimlidir (kutulama maliyeti yoktur) ve hazır yardımcılar sunar: sum(), average(), max() ve hepsini tek geçişte veren summaryStatistics():

IntSummaryStatistics ist = sayilar.stream()
        .mapToInt(Integer::intValue).summaryStatistics();
ist.getAverage(); ist.getMax(); // ...

Örnek 2 (./Ornek2.java) reduce, IntStream, summaryStatistics ve IntStream.rangeClosed ile sayı üretimini gösterir.

Collectors: toplama ve gruplama

collect, akışın sonucunu bir yapıya dönüştürür. Collectors sınıfı bunun için güçlü yardımcılar sunar:

  • toList() / toSet() — listeye/kümeye topla
  • joining(", ", "[", "]") — metinleri birleştir
  • groupingBy(...) — bir ölçüte göre Map halinde grupla
  • summingDouble(...), counting(), averagingInt(...) — gruplarda toplama
  • partitioningBy(...) — bir koşula göre ikiye ayır (true/false)
Map<String, Double> kategoriToplam = urunler.stream()
        .collect(Collectors.groupingBy(Urun::kategori,
                 Collectors.summingDouble(Urun::fiyat)));

Örnek 3 (./Ornek3.java) ürünleri kategoriye göre gruplar, kategori bazında toplam fiyatı hesaplar, joining ile birleştirir ve partitioningBy ile pahalı/uygun olarak ayırır.

Ne zaman stream, ne zaman döngü?

Stream'ler okunabilirliği artırır ve niyeti netleştirir; özellikle filtreleme/dönüştürme/ gruplama zincirlerinde parlar. Ama her şey için zorlamana gerek yok: basit bir sayaç veya yan etkili bir döngü için klasik for daha açık olabilir. Ayrıca stream'ler tek kullanımlıktır (bir kez tüketilince yeniden kullanılamaz).

Özet

Stream API ile veriyi akış olarak düşünüp filtreleme, dönüştürme, sıralama, indirgeme ve gruplama işlemlerini zincirleme yazmayı öğrendik. Collectors ile sonucu listeye/haritaya topladık. Bu, modern Java kodunun her yerinde karşına çıkacak. Sırada, null ile başa çıkmanın güvenli yolu: Optional.

Kod Örnekleri(3)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Stream temelleri — filter, map, sorted, collect.
2// Çalıştırma: java Ornek1.java
3import java.util.List;
4import java.util.stream.Collectors;
5
6public class Ornek1 {
7
8    public static void main(String[] args) {
9        List<String> isimler = List.of("ada", "burak", "ceren", "deniz", "emir", "ayse");
10
11        // Bir stream pipeline'ı: kaynak -> ara işlemler -> sonlandırıcı.
12        // 'a' ile başlayanları al, büyük harfe çevir, sıralı bir listeye topla.
13        List<String> sonuc = isimler.stream()
14                .filter(ad -> ad.startsWith("a"))   // ara işlem: filtrele
15                .map(String::toUpperCase)           // ara işlem: dönüştür
16                .sorted()                           // ara işlem: sırala
17                .collect(Collectors.toList());      // sonlandırıcı: topla
18
19        System.out.println("Kaynak  : " + isimler);
20        System.out.println("Sonuç   : " + sonuc);
21
22        // forEach: her elemanı tüket (yan etki).
23        System.out.println("\nUzunluğu 5 olanlar:");
24        isimler.stream()
25                .filter(ad -> ad.length() == 5)
26                .forEach(ad -> System.out.println("  - " + ad));
27
28        // count, anyMatch, allMatch gibi sonlandırıcılar:
29        long aIleBaslayan = isimler.stream().filter(a -> a.startsWith("a")).count();
30        boolean hepsiKisa = isimler.stream().allMatch(a -> a.length() <= 6);
31        System.out.println("\n'a' ile başlayan sayısı: " + aIleBaslayan);
32        System.out.println("Hepsi <= 6 harf mi?    : " + hepsiKisa);
33
34        // distinct + limit
35        System.out.println("\nİlk 3 benzersiz uzunluk: " +
36                isimler.stream().map(String::length).distinct().limit(3).collect(Collectors.toList()));
37    }
38}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: reduce, IntStream ve istatistikler.
2// Çalıştırma: java Ornek2.java
3import java.util.List;
4import java.util.IntSummaryStatistics;
5
6public class Ornek2 {
7
8    public static void main(String[] args) {
9        List<Integer> sayilar = List.of(4, 8, 15, 16, 23, 42);
10
11        // --- reduce: elemanları tek bir değere indirger ---
12        int toplam = sayilar.stream().reduce(0, Integer::sum);
13        int carpim = sayilar.stream().reduce(1, (a, b) -> a * b);
14        System.out.println("Sayılar: " + sayilar);
15        System.out.println("reduce toplam : " + toplam);
16        System.out.println("reduce çarpım : " + carpim);
17
18        // --- IntStream: ilkel int akışı, kutulama maliyeti yok + hazır istatistikler ---
19        int kareToplam = sayilar.stream()
20                .mapToInt(Integer::intValue)   // Stream<Integer> -> IntStream
21                .map(n -> n * n)
22                .sum();
23        System.out.println("\nKarelerin toplamı: " + kareToplam);
24
25        // summaryStatistics: tek geçişte sayı/min/max/ortalama/toplam
26        IntSummaryStatistics ist = sayilar.stream().mapToInt(Integer::intValue).summaryStatistics();
27        System.out.printf("adet=%d, min=%d, max=%d, ortalama=%.2f, toplam=%d%n",
28                ist.getCount(), ist.getMin(), ist.getMax(), ist.getAverage(), ist.getSum());
29
30        // IntStream.range ile sayı üretimi (1..10 toplamı)
31        int birdenOna = java.util.stream.IntStream.rangeClosed(1, 10).sum();
32        System.out.println("\n1..10 toplamı (IntStream.rangeClosed): " + birdenOna);
33
34        // max ve filtreli işlem
35        sayilar.stream().filter(n -> n % 2 == 0).max(Integer::compareTo)
36                .ifPresent(m -> System.out.println("En büyük çift: " + m));
37    }
38}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek3

çalıştırılabilir
Ornek3.java
1// Ornek3: Collectors — groupingBy, joining ve gruplu toplamlar.
2// Çalıştırma: java Ornek3.java
3import java.util.List;
4import java.util.Map;
5import java.util.stream.Collectors;
6
7public class Ornek3 {
8
9    record Urun(String ad, String kategori, double fiyat) {}
10
11    public static void main(String[] args) {
12        List<Urun> urunler = List.of(
13                new Urun("Klavye", "Aksesuar", 450),
14                new Urun("Mouse", "Aksesuar", 250),
15                new Urun("Monitör", "Ekran", 3200),
16                new Urun("Laptop", "Bilgisayar", 28000),
17                new Urun("Kulaklık", "Aksesuar", 900),
18                new Urun("TV", "Ekran", 12000)
19        );
20
21        // joining: metinleri tek bir String'de birleştir.
22        String adListesi = urunler.stream()
23                .map(Urun::ad)
24                .collect(Collectors.joining(", ", "[", "]"));
25        System.out.println("Ürünler: " + adListesi);
26
27        // groupingBy: kategoriye göre grupla -> Map<kategori, List<Urun>>
28        Map<String, List<Urun>> kategoriyeGore = urunler.stream()
29                .collect(Collectors.groupingBy(Urun::kategori));
30        System.out.println("\nKategori -> ürün sayısı:");
31        kategoriyeGore.forEach((k, v) -> System.out.println("  " + k + " : " + v.size()));
32
33        // groupingBy + summingDouble: kategori bazında toplam fiyat
34        Map<String, Double> kategoriToplam = urunler.stream()
35                .collect(Collectors.groupingBy(Urun::kategori, Collectors.summingDouble(Urun::fiyat)));
36        System.out.println("\nKategori bazında toplam fiyat:");
37        kategoriToplam.forEach((k, v) -> System.out.printf("  %-12s : %,.2f TL%n", k, v));
38
39        // partitioningBy: bir koşula göre ikiye ayır (true/false)
40        Map<Boolean, List<Urun>> pahaliMi = urunler.stream()
41                .collect(Collectors.partitioningBy(u -> u.fiyat() >= 1000));
42        System.out.println("\nPahalı (>=1000) ürün sayısı: " + pahaliMi.get(true).size());
43        System.out.println("Uygun (<1000) ürün sayısı  : " + pahaliMi.get(false).size());
44    }
45}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.