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:
- Kaynak:
list.stream(),Arrays.stream(...),IntStream.range(...) - Ara işlemler (intermediate):
filter,map,sorted,distinct,limit— yeni bir stream döndürür, tembeldir (lazy). - 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 toplajoining(", ", "[", "]")— metinleri birleştirgroupingBy(...)— bir ölçüte göreMaphalinde gruplasummingDouble(...),counting(),averagingInt(...)— gruplarda toplamapartitioningBy(...)— 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ılabilir1// 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}Ornek2
çalıştırılabilir1// 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}Ornek3
çalıştırılabilir1// 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}