Java 23 — Sürüm Analizi ve Özellikler
> Bu doküman Java 23 sürümünü Türkçe olarak detaylı şekilde açıklar. Hedef kitle: > Java geliştiricileri, eğitmenler ve sürümler arası geçişi planlayanlar.
Java 23 — Sürüm Analizi ve Özellikler
Bu doküman Java 23 sürümünü Türkçe olarak detaylı şekilde açıklar. Hedef kitle: Java geliştiricileri, eğitmenler ve sürümler arası geçişi planlayanlar.
1. Sürüm Bilgisi
| Özellik | Değer |
|---|---|
| Sürüm | Java SE 23 (JDK 23) |
| Çıkış Tarihi | 17 Eylül 2024 |
| Destek Türü | non-LTS (Long Term Support DEĞİL) |
| Önceki LTS | Java 21 (Eylül 2023) |
| Sonraki LTS | Java 25 (Eylül 2025 — planlanan) |
| Destek Süresi | Genellikle 6 ay (bir sonraki feature sürümüne kadar) |
| JDK Şeması | 6 aylık feature-release kadansı devam ediyor |
Önemli not — non-LTS ne demek? Java 23 bir "feature release" (özellik sürümü) olup yalnızca bir sonraki sürüm (Java 24) çıkana kadar (yaklaşık 6 ay) güncelleme alır. Production ortamları için genellikle LTS sürümleri (21, 25, ...) tercih edilir. Java 23, yeni özellikleri denemek ve preview API'lerle çalışmak için idealdir.
Preview Durumu Açıklaması
Java'da bir özellik birden çok aşamadan geçer:
- Preview (1., 2., 3. tur) — Tasarımı tamamlanmış ama henüz kalıcı olmayan,
geri bildirimle değişebilen özellikler. Kullanmak için derleme ve çalıştırmada
--enable-previewbayrağı gerekir. - Final / Standart — Kalıcı hale gelmiş, bayrak gerektirmeyen özellikler.
Java 23'te preview özelliklerini derlemek için:
# Derleme javac --release 23 --enable-preview Ornek.java # Çalıştırma java --enable-preview Ornek
2. Java 23 JEP'leri (Genel Bakış)
| JEP | Özellik | Durum |
|---|---|---|
| JEP 467 | Markdown Documentation Comments | Final / Kalıcı (preview değil) |
| JEP 473 | Stream Gatherers | 2. Preview |
| JEP 480 | Structured Concurrency | 3. Preview |
| JEP 481 | Scoped Values | 3. Preview |
| JEP 455 | Primitive Types in Patterns, instanceof, switch | Preview |
| JEP 474 | ZGC: Generational Mode by Default | Final (davranış değişikliği) |
| JEP 476 | Module Import Declarations | Preview |
| JEP 482 | Flexible Constructor Bodies | 2. Preview |
| JEP 471 | Deprecate the Memory-Access Methods in sun.misc.Unsafe | Final |
Bu README her bir önemli özelliği aşağıda detaylandırır. Bazı özellikler kendi ayrı dosyalarında derinlemesine ele alınmıştır:
- Markdown Javadoc →
MarkdownJavadoc.md- Stream Gatherers (gelişmiş örnekler) →
StreamGatherersGelismis.java- Module Import Declarations →
ModuleImportDeclarations.md
3. JEP 467 — Markdown Documentation Comments (Final)
Detaylı örnekler için bkz.
MarkdownJavadoc.md
NEDİR?
Javadoc yorumlarını artık HTML + @tag karışımı yerine Markdown ile
yazabilmenizi sağlayan özellik. Yeni yorum sözdizimi /// (üç eğik çizgi) ile
başlar.
/// Bu bir **Markdown** Javadoc yorumudur. /// - Madde 1 /// - Madde 2 /// /// Bağlantı: [String] public class Ornek { }
NEDEN GELDİ?
Klasik Javadoc HTML tabanlıdır; <p>, <ul>, <code> gibi etiketler okunması
zor ve hata yapmaya açıktır. Markdown çok daha okunabilir ve geliştiricilerin
zaten README'lerde kullandığı bir formattır.
NE İŞE YARAR?
- Dokümantasyon yorumlarını kaynak kodda daha okunabilir hale getirir.
- HTML kaçış karakterleriyle (
<,>) uğraşmayı azaltır. - Kod blokları, listeler, tablolar, bağlantılar daha kolay yazılır.
NEREDE KOLAYLIK SAĞLAR?
API dokümantasyonu yazan kütüphane geliştiricileri, dahili kod dokümantasyonu tutan ekipler.
ESKİ vs YENİ (kısa)
// ESKİ (HTML tabanlı) /** * Toplama yapar. * <p>Örnek: <code>topla(2, 3)</code> → 5</p> * <ul><li>Negatif sayı kabul eder</li></ul> */ // YENİ (Markdown tabanlı) /// Toplama yapar. /// /// Örnek: `topla(2, 3)` → 5 /// /// - Negatif sayı kabul eder
GERÇEK HAYAT ÖRNEĞİ
Bir açık kaynak kütüphane (örn. bir JSON parser) API dokümantasyonunu Markdown'a geçirerek hem kaynak kodda hem de üretilen HTML'de daha temiz dokümantasyon sunar.
4. JEP 473 — Stream Gatherers (2. Preview)
Çalıştırılabilir gelişmiş örnekler için bkz.
StreamGatherersGelismis.java
NEDİR?
Stream API'sine özel ara (intermediate) operasyonlar tanımlama yeteneği
ekleyen özellik. stream.gather(...) metodu ile Gatherer arayüzünü kullanırız.
Collectors'ın terminal operasyonlar için yaptığını, Gatherer ara operasyonlar
için yapar.
NEDEN GELDİ?
map, filter, flatMap gibi yerleşik ara operasyonlar her ihtiyacı
karşılamıyordu. Örneğin "kayan pencere" (sliding window), "çalışan toplam"
(running sum), gruplayıp tekilleştirme gibi durumsal (stateful) operasyonları
standart API ile yazmak zordu.
NE İŞE YARAR?
- Durumlu (stateful) ara operasyonlar yazılabilir.
- Yerleşik fabrikalar:
Gatherers.windowFixed(n),Gatherers.windowSliding(n),Gatherers.fold(...),Gatherers.scan(...),Gatherers.mapConcurrent(...). - Birden çok elemanı tek bir elemana ya da bir elemanı birden çoğa dönüştürebilir.
NEREDE KOLAYLIK SAĞLAR?
Veri işleme hatları (data pipelines), zaman serisi analizi (hareketli ortalama), olay akışı işleme, batch'leme.
ESKİ vs YENİ
// ESKİ: Kayan pencere için elle index yönetimi gerekirdi List<List<Integer>> pencereler = new ArrayList<>(); for (int i = 0; i + 3 <= liste.size(); i++) { pencereler.add(liste.subList(i, i + 3)); } // YENİ: Tek satır var pencereler = liste.stream() .gather(Gatherers.windowSliding(3)) .toList();
GERÇEK HAYAT ÖRNEĞİ
Borsa fiyatlarının 5 günlük hareketli ortalamasını hesaplamak:
fiyatlar.stream().gather(Gatherers.windowSliding(5)).map(ortalama).toList().
Detaylı örnek .java dosyasındadır.
AVANTAJ / DEZAVANTAJ / RİSK
- Avantaj: Çok daha okunabilir, yeniden kullanılabilir akış operasyonları.
- Dezavantaj/Risk: Hâlâ preview — API imzaları değişebilir,
--enable-previewgerektirir, production'da dikkatli kullanılmalı.
5. JEP 480 — Structured Concurrency (3. Preview)
NEDİR?
Birden fazla eşzamanlı görevi tek bir iş birimi gibi yöneten API.
java.util.concurrent.StructuredTaskScope ile alt görevler bir kapsam (scope)
içinde başlatılır, hepsi birlikte tamamlanır veya birlikte iptal edilir.
NEDEN GELDİ?
Klasik ExecutorService + Future kullanımında görev sızıntısı (leak),
iptal yönetimi ve hata yayılımı (error propagation) hataya açıktır. Bir görev
başarısız olsa bile diğerleri arka planda boşuna çalışmaya devam edebilir.
NE İŞE YARAR?
- Alt görevlerin yaşam döngüsünü kod bloğunun yapısına bağlar (try-with-resources gibi).
- "Birini başaranı bekle" (
ShutdownOnSuccess) ya da "ilk hatada hepsini iptal et" (ShutdownOnFailure) gibi politikalar sunar.
NEREDE KOLAYLIK SAĞLAR?
Mikroservis çağrılarının paralel yürütülmesi, fan-out/fan-in desenleri, birden çok kaynaktan veri toplama.
ESKİ vs YENİ
// ESKİ ExecutorService ex = Executors.newVirtualThreadPerTaskExecutor(); Future<String> kullanici = ex.submit(() -> kullaniciGetir()); Future<Integer> siparis = ex.submit(() -> siparisGetir()); // İptal/hata yönetimi elle, sızıntı riski yüksek // YENİ (preview) try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { var kullanici = scope.fork(() -> kullaniciGetir()); var siparis = scope.fork(() -> siparisGetir()); scope.join().throwIfFailed(); return birlestir(kullanici.get(), siparis.get()); }
GERÇEK HAYAT ÖRNEĞİ
Bir ürün detay sayfası; ürün bilgisi, stok durumu ve yorumları üç ayrı servisten paralel çeker. Biri başarısız olursa hepsi iptal edilir, kaynak israfı olmaz.
Risk: Preview API.
StructuredTaskScopeimzası sürümler arasında değişebilir (nitekim ileri sürümlerde değişti).--enable-previewşarttır.
6. JEP 481 — Scoped Values (3. Preview)
NEDİR?
Bir veriyi, onu açıkça parametre olarak geçirmeden, belirli bir yürütme kapsamı
(ve onun alt görevleri) içinde paylaşmayı sağlayan değişmez (immutable) yapı.
ScopedValue sınıfı kullanılır.
NEDEN GELDİ?
ThreadLocal'ın problemleri vardı: değiştirilebilir (mutable), temizlenmesi
zor (memory leak riski), milyonlarca sanal iş parçacığında (virtual thread)
maliyetli. ScopedValue bu sorunları çözer.
NE İŞE YARAR?
- Değer değişmezdir;
where(...).run(...)bloğu boyunca geçerlidir. - Blok bittiğinde otomatik temizlenir.
- Sanal iş parçacıklarıyla verimli çalışır, structured concurrency ile uyumludur.
NEREDE KOLAYLIK SAĞLAR?
İstek bağlamı (request context), kullanıcı kimliği, transaction ID gibi "ortam" bilgisini metot zincirleri boyunca taşımak.
ESKİ vs YENİ
// ESKİ: ThreadLocal static final ThreadLocal<User> CURRENT = new ThreadLocal<>(); CURRENT.set(user); try { isYap(); } finally { CURRENT.remove(); } // unutulursa leak! // YENİ: ScopedValue (preview) static final ScopedValue<User> CURRENT = ScopedValue.newInstance(); ScopedValue.where(CURRENT, user).run(() -> isYap()); // otomatik temizlik
GERÇEK HAYAT ÖRNEĞİ
Web sunucusunda gelen her HTTP isteği için kimliği doğrulanmış kullanıcıyı
ScopedValue ile tutmak; istek işleme zincirindeki tüm metotlar bu değere
güvenli ve değişmez şekilde erişir.
Risk: Preview.
--enable-previewgerekir.
7. JEP 455 — Primitive Types in Patterns, instanceof, switch (Preview)
NEDİR?
İlkel (primitive) tiplerin (int, long, double, ...) artık instanceof,
desen eşleştirme (pattern matching) ve switch içinde kullanılabilmesi.
NEDEN GELDİ?
Önceki desen eşleştirme yalnızca referans tipleri destekliyordu. İlkel tipler için tip kontrolü ve güvenli dönüşüm (özellikle daralma — narrowing) dil seviyesinde yoktu.
NE İŞE YARAR?
if (x instanceof int i)gibi ilkel tip kontrolü.switchiçinde ilkel desenler ve daralma kontrolleri (örn. birintdeğerbytearalığına sığıyor mu?).
ESKİ vs YENİ
// ESKİ: elle aralık kontrolü int x = 200; if (x >= Byte.MIN_VALUE && x <= Byte.MAX_VALUE) { byte b = (byte) x; } // YENİ (preview): desen ile güvenli daralma if (x instanceof byte b) { // x, byte aralığına sığıyorsa b atanır }
GERÇEK HAYAT ÖRNEĞİ
Bir veri dönüştürme katmanında, gelen sayısal değerlerin hedef ilkel tipe güvenli şekilde sığıp sığmadığını desenlerle kontrol etmek.
Risk: Preview.
--enable-previewgerekir.
8. JEP 474 — ZGC: Generational Mode by Default (Final)
NEDİR?
Z Garbage Collector (ZGC) artık varsayılan olarak generational (kuşaksal) modda çalışır. Eski (non-generational) mod hâlâ seçilebilir ama önerilmez.
NEDEN GELDİ?
Generational hipotezi ("çoğu nesne genç ölür") sayesinde nesneleri genç (young) ve yaşlı (old) kuşaklara ayırarak GC daha az CPU ve bellek harcar. Java 21'de generational ZGC eklenmişti; Java 23'te bu artık varsayılan.
NE İŞE YARAR?
- Çok büyük heap'lerde (TB seviyesi) düşük duraklama (low pause) süreleri.
- Daha az bellek ve CPU yükü, daha yüksek verim (throughput).
ESKİ vs YENİ
# Java 21–22: generational ZGC açıkça istenirdi java -XX:+UseZGC -XX:+ZGenerational uygulama # Java 23: -XX:+UseZGC zaten generational; non-generational deprecated java -XX:+UseZGC uygulama
NEREDE KOLAYLIK SAĞLAR?
Büyük bellekli, düşük gecikme gerektiren sunucu uygulamaları (örn. büyük cache, gerçek zamanlı işleme).
Geçişte ne değişti:
-XX:+ZGenerationalbayrağı artık gereksiz (ve deprecated). ZGC kullanan uygulamalar otomatik olarak kuşaksal modda çalışır.
9. JEP 476 — Module Import Declarations (Preview)
Detaylı anlatım için bkz.
ModuleImportDeclarations.md
NEDİR?
import module M; sözdizimi ile bir modülün export ettiği tüm paketleri tek
satırda içe aktarma. Örneğin import module java.base; ile java.util,
java.io, java.util.stream vb. tüm paketler kullanılabilir hale gelir.
NEDEN GELDİ?
Özellikle eğitim, prototipleme ve script benzeri kullanımda onlarca import
satırı yazmak yorucuydu. Modül seviyesinde toplu import bunu basitleştirir.
ESKİ vs YENİ
// ESKİ import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.time.LocalDate; // YENİ (preview) import module java.base;
GERÇEK HAYAT ÖRNEĞİ
Yeni başlayanların ilk programları, hızlı prototipler veya tek dosyalık
çalıştırılabilir kaynak (java Ornek.java) senaryoları.
Risk: Preview. İsim çakışmaları (aynı isimli sınıflar farklı paketlerde) belirsizlik yaratabilir; bu durumda tek tek import gerekir.
--enable-previewgerekir. DetaylarModuleImportDeclarations.mddosyasında.
10. JEP 482 — Flexible Constructor Bodies (2. Preview)
NEDİR?
Bir yapıcıda (constructor) super(...) veya this(...) çağrısından önce
deyimler (statement) çalıştırabilme imkânı. Önceden bu çağrı yapıcının ilk
deyimi olmak zorundaydı.
NEDEN GELDİ?
super() çağrısından önce argümanları doğrulamak (validation), hazırlamak ya da
normalize etmek mümkün değildi; bu yüzden statik yardımcı metotlar gibi geçici
çözümler (workaround) kullanılırdı.
NE İŞE YARAR?
super(...)/this(...)öncesinde argüman doğrulama yapılabilir ("prologue" / ön bölge).- Hatalı argümanlar üst sınıf yapıcısı çalışmadan yakalanır — daha güvenli.
ESKİ vs YENİ
// ESKİ: doğrulama super'den sonra ya da statik metotla class Kullanici extends Kisi { Kullanici(String ad) { super(ad); // önce zorunlu if (ad == null) throw ...; // doğrulama sonra (geç!) } } // YENİ (preview): super'den önce doğrulama class Kullanici extends Kisi { Kullanici(String ad) { if (ad == null || ad.isBlank()) // önce doğrula throw new IllegalArgumentException("ad bos olamaz"); super(ad); // sonra üst yapıcı } }
Not:
super(...)öncesindeki "prologue" bölgesinde henüzthisörneğinin alanlarına/metotlarına erişilemez (örnek tam oluşmadığı için), sadece hesaplama/doğrulama yapılabilir.
GERÇEK HAYAT ÖRNEĞİ
Bir PositiveNumber extends Number sınıfında, üst yapıcıya değer verilmeden
önce negatif değerin reddedilmesi.
Risk: Preview.
--enable-previewgerekir.
11. Geçiş Notları (Java 21/22 → Java 23)
Avantajlar
- Daha temiz dokümantasyon (Markdown Javadoc — kalıcı, risksiz).
- ZGC kullananlar için otomatik performans iyileştirmesi (generational varsayılan).
- Yeni dil özelliklerini (gatherers, module import, primitive patterns) deneme imkânı.
Dezavantajlar / Riskler
- non-LTS: Yalnızca ~6 ay destek; production için 25'i (LTS) beklemek mantıklı.
- Birçok yeni özellik preview — API değişebilir,
--enable-previewzorunlu, production'da kullanımı önerilmez. sun.misc.Unsafebellek erişim metotları deprecated (JEP 471) — bunlara bağımlı eski kütüphaneler ileride sorun yaşayabilir; alternatif olarakVarHandle/MemorySegment(FFM API) önerilir.
Genel Tavsiye
- Öğrenme/deneme: Java 23 mükemmel — yeni özellikleri deneyin.
- Production: Stabil iş yükleri için LTS (21 veya 25) tercih edin; preview özelliklere bel bağlamayın.
12. İlgili Dosyalar
| Dosya | İçerik |
|---|---|
MarkdownJavadoc.md | Markdown Javadoc (JEP 467) detaylı, eski/yeni yan yana |
StreamGatherersGelismis.java | Stream Gatherers gelişmiş, çalıştırılabilir örnekler |
ModuleImportDeclarations.md | Module Import Declarations (JEP 476) detaylı |
Hazırlanma tarihi: 2026 — Java 23 (Eylül 2024) baz alınmıştır.
▶ Kod Örnekleri(1)
StreamGatherersGelismis
ortam gerekir1// =============================================================================
2// Java 23 - Stream Gatherers (Gelismis Ornekler) - JEP 473 (2. Preview)
3// =============================================================================
4//
5// Stream Gatherers, Stream API'sine OZEL ARA (intermediate) operasyonlar
6// eklemenizi saglar. Collectors'in terminal operasyonlar icin yaptigini,
7// Gatherer ara operasyonlar icin yapar.
8//
9// Bu dosya PREVIEW ozelligi kullanir. Derlemek ve calistirmak icin:
10//
11// javac --release 23 --enable-preview StreamGatherersGelismis.java
12// java --enable-preview StreamGatherersGelismis
13//
14// (Tek dosya modunda: java --release 23 --enable-preview StreamGatherersGelismis.java)
15//
16// Yerlesik (built-in) gatherer fabrikalari (java.util.stream.Gatherers):
17// - windowFixed(n) : ardisik, ortusmeyen n'lik pencereler
18// - windowSliding(n) : 1 kayan, ortusen n'lik pencereler
19// - fold(...) : tek bir sonuca katlar (terminal benzeri)
20// - scan(...) : her adimda biriken (running) sonucu yayar
21// - mapConcurrent(...) : sinirli paralellikle map
22//
23// Ayrica Gatherer arayuzunu uygulayarak KENDI gatherer'inizi yazabilirsiniz.
24// =============================================================================
25
26import java.util.ArrayList;
27import java.util.LinkedHashSet;
28import java.util.List;
29import java.util.Set;
30import java.util.stream.Gatherer;
31import java.util.stream.Gatherers;
32import java.util.stream.Stream;
33
34public class StreamGatherersGelismis {
35
36 // -------------------------------------------------------------------------
37 // main: tum ornekleri sirayla calistirir
38 // -------------------------------------------------------------------------
39 public static void main(String[] args) {
40 System.out.println("=== Java 23 Stream Gatherers - Gelismis Ornekler ===\n");
41
42 ornek1_hareketliOrtalama();
43 ornek2_calisanToplam_scan();
44 ornek3_sabitPencere_batchleme();
45 ornek4_ozelDedup();
46 ornek5_ardisikGruplama();
47 ornek6_limitliAlma();
48
49 System.out.println("\n=== Tum ornekler tamamlandi ===");
50 }
51
52 // =========================================================================
53 // ORNEK 1: HAREKETLI ORTALAMA (Moving Average) - windowSliding
54 // -------------------------------------------------------------------------
55 // Senaryo: Bir hissenin gunluk kapanis fiyatlari var. 3 gunluk hareketli
56 // ortalamayi (moving average) hesaplamak istiyoruz. Bu, zaman serisi
57 // analizinde cok yaygindir (trendi yumusatmak icin).
58 //
59 // windowSliding(3): [g1,g2,g3], [g2,g3,g4], [g3,g4,g5] ... pencereleri verir.
60 // =========================================================================
61 static void ornek1_hareketliOrtalama() {
62 System.out.println("--- Ornek 1: 3 Gunluk Hareketli Ortalama (windowSliding) ---");
63
64 List<Double> fiyatlar = List.of(100.0, 102.0, 101.0, 105.0, 110.0, 108.0, 112.0);
65
66 List<Double> hareketliOrtalama = fiyatlar.stream()
67 // 3'luk kayan pencereler olustur: her pencere bir List<Double>
68 .gather(Gatherers.windowSliding(3))
69 // her pencerenin ortalamasini al
70 .map(pencere -> pencere.stream()
71 .mapToDouble(Double::doubleValue)
72 .average()
73 .orElse(0.0))
74 .toList();
75
76 System.out.println("Fiyatlar : " + fiyatlar);
77 System.out.println("3 gunluk ort. : " + hareketliOrtalama);
78 System.out.println();
79 }
80
81 // =========================================================================
82 // ORNEK 2: CALISAN TOPLAM (Running Total) - scan
83 // -------------------------------------------------------------------------
84 // Senaryo: Bir banka hesabinin gunluk islem tutarlari var. Her islemden
85 // SONRAKI bakiyeyi (kumulatif toplam) gormek istiyoruz.
86 //
87 // scan(baslangic, biriktirici): her elemanda biriken sonucu YAYAR.
88 // fold'dan farki: fold tek bir nihai sonuc verir, scan ara sonuclari da verir.
89 // =========================================================================
90 static void ornek2_calisanToplam_scan() {
91 System.out.println("--- Ornek 2: Calisan Toplam / Kumulatif Bakiye (scan) ---");
92
93 List<Integer> islemler = List.of(100, -30, 50, -20, 200); // + yatirma, - cekme
94
95 List<Integer> bakiyeler = islemler.stream()
96 .gather(Gatherers.scan(
97 () -> 0, // baslangic bakiyesi
98 (bakiye, islem) -> bakiye + islem)) // her adimda biriktir
99 .toList();
100
101 System.out.println("Islemler : " + islemler);
102 System.out.println("Bakiye gecmisi : " + bakiyeler); // [100, 70, 120, 100, 300]
103 System.out.println();
104 }
105
106 // =========================================================================
107 // ORNEK 3: SABIT PENCERE / BATCH'LEME - windowFixed
108 // -------------------------------------------------------------------------
109 // Senaryo: 1000'lerce kaydi toplu (batch) olarak isleyecegiz. Veritabanina
110 // ya da bir API'ye tek tek degil, 4'luk gruplar halinde gondermek istiyoruz
111 // (ag turunu / commit sayisini azaltmak icin).
112 //
113 // windowFixed(4): [1,2,3,4], [5,6,7,8], [9,10] (son grup kismi olabilir).
114 // =========================================================================
115 static void ornek3_sabitPencere_batchleme() {
116 System.out.println("--- Ornek 3: Toplu (Batch) Isleme (windowFixed) ---");
117
118 List<Integer> kayitlar = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
119
120 List<List<Integer>> gruplar = kayitlar.stream()
121 .gather(Gatherers.windowFixed(4))
122 .toList();
123
124 System.out.println("Tum kayitlar : " + kayitlar);
125 int no = 1;
126 for (List<Integer> grup : gruplar) {
127 System.out.println(" Batch " + (no++) + " gonderiliyor: " + grup);
128 }
129 System.out.println();
130 }
131
132 // =========================================================================
133 // ORNEK 4: OZEL GATHERER - SIRAYI KORUYAN TEKILLESTIRME (distinct)
134 // -------------------------------------------------------------------------
135 // Senaryo: Bir log akisindan tekrar eden kullanici ID'lerini ayikliyoruz
136 // ama ilk gorulme sirasini korumak istiyoruz.
137 //
138 // Burada KENDI gatherer'imizi yaziyoruz. Durumlu (stateful) bir gatherer:
139 // daha once gorulen elemanlari bir Set'te tutar, yeni olani downstream'e iletir.
140 //
141 // Gatherer.ofSequential(initializer, integrator):
142 // - initializer : durum nesnesini (Set) olusturur
143 // - integrator : her eleman icin calisir; downstream.push(...) ile yayar
144 // =========================================================================
145 static void ornek4_ozelDedup() {
146 System.out.println("--- Ornek 4: Ozel Gatherer ile Sira Koruyan Dedup ---");
147
148 List<String> kullaniciAkisi = List.of(
149 "ali", "veli", "ali", "ayse", "veli", "ali", "fatma");
150
151 List<String> tekil = kullaniciAkisi.stream()
152 .gather(siraKoruyanTekil())
153 .toList();
154
155 System.out.println("Ham akis : " + kullaniciAkisi);
156 System.out.println("Tekil (sirali) : " + tekil); // [ali, veli, ayse, fatma]
157 System.out.println();
158 }
159
160 /**
161 * Ilk gorulme sirasini koruyarak tekrar edenleri eleyen ozel gatherer.
162 * (Standart .distinct() ile ayni sonucu verir ama gatherer yazimini gosterir.)
163 */
164 static <T> Gatherer<T, ?, T> siraKoruyanTekil() {
165 return Gatherer.ofSequential(
166 // 1) Durum (state): daha once gorulen elemanlar
167 LinkedHashSet::new,
168 // 2) Integrator: her eleman icin calisir
169 (Set<T> gorulenler, T eleman, Gatherer.Downstream<? super T> downstream) -> {
170 if (gorulenler.add(eleman)) { // ilk kez goruluyorsa true doner
171 return downstream.push(eleman); // asagiya ilet
172 }
173 return true; // tekrar eden -> iletme, ama akisa devam et
174 });
175 }
176
177 // =========================================================================
178 // ORNEK 5: OZEL GATHERER - ARDISIK ESIT ELEMANLARI GRUPLA (run-length)
179 // -------------------------------------------------------------------------
180 // Senaryo: Bir sensorden gelen durum akisinda, ARDISIK ayni degerleri
181 // tek bir grupta toplamak istiyoruz (run-length encoding mantigi).
182 // Ornek: [A,A,B,B,B,A,C] -> [[A,A],[B,B,B],[A],[C]]
183 //
184 // Bu gatherer durumludur (acik grubu tutar) ve AKIS SONUNDA kalan grubu
185 // yaymak icin bir "finisher" kullanir.
186 // =========================================================================
187 static void ornek5_ardisikGruplama() {
188 System.out.println("--- Ornek 5: Ozel Gatherer ile Ardisik Gruplama ---");
189
190 List<String> durumlar = List.of("A", "A", "B", "B", "B", "A", "C", "C");
191
192 List<List<String>> gruplar = durumlar.stream()
193 .gather(ardisikGrupla())
194 .toList();
195
196 System.out.println("Durum akisi : " + durumlar);
197 System.out.println("Ardisik gruplar : " + gruplar);
198 System.out.println();
199 }
200
201 /**
202 * Ardisik (consecutive) esit elemanlari ayni gruba toplar.
203 * Durum: o anki acik grup (List). Eleman onceki ile esitse gruba ekler,
204 * degilse onceki grubu yayar ve yeni grup baslatir. Sonda kalan grup
205 * finisher ile yayilir.
206 */
207 static <T> Gatherer<T, ?, List<T>> ardisikGrupla() {
208 // Durumu tutmak icin tek elemanli bir tasiyici dizi kullaniyoruz
209 // (lambda icinde mutable referans icin pratik bir yontem).
210 return Gatherer.<T, List<List<T>>, List<T>>ofSequential(
211 // initializer: tek elemanli liste; index 0 = "o anki acik grup"
212 () -> {
213 List<List<T>> kutu = new ArrayList<>();
214 kutu.add(new ArrayList<>()); // acik grup
215 return kutu;
216 },
217 // integrator
218 (kutu, eleman, downstream) -> {
219 List<T> acikGrup = kutu.get(0);
220 if (acikGrup.isEmpty() || acikGrup.get(0).equals(eleman)) {
221 // ilk eleman ya da onceki ile ayni -> mevcut gruba ekle
222 acikGrup.add(eleman);
223 } else {
224 // farkli -> mevcut grubu yay, yeni grup baslat
225 boolean devam = downstream.push(List.copyOf(acikGrup));
226 List<T> yeniGrup = new ArrayList<>();
227 yeniGrup.add(eleman);
228 kutu.set(0, yeniGrup);
229 if (!devam) return false;
230 }
231 return true;
232 },
233 // finisher: akis bitince son acik grubu yay
234 (kutu, downstream) -> {
235 List<T> acikGrup = kutu.get(0);
236 if (!acikGrup.isEmpty()) {
237 downstream.push(List.copyOf(acikGrup));
238 }
239 });
240 }
241
242 // =========================================================================
243 // ORNEK 6: OZEL GATHERER - KOSULA GORE ERKEN KESME (short-circuit)
244 // -------------------------------------------------------------------------
245 // Senaryo: Bir sensorden gelen sonsuz/uzun bir akista, toplam belirli bir
246 // esigi gecene KADAR elemanlari al, esik asilinca akisi DURDUR.
247 //
248 // Gatherer integrator'unde downstream.push(...) FALSE dondurursek ya da
249 // biz false dondururek akisi kisa devre (short-circuit) yaptirabiliriz.
250 // Bu, sonsuz Stream'lerle bile guvenli calisir.
251 // =========================================================================
252 static void ornek6_limitliAlma() {
253 System.out.println("--- Ornek 6: Ozel Gatherer ile Toplam Esigine Kadar Al ---");
254
255 // Sonsuz bir akis: 1, 2, 3, 4, ... (sadece esik mantigini test icin)
256 List<Integer> alinanlar = Stream.iterate(1, n -> n + 1)
257 .gather(toplamEsigineKadar(15)) // toplam 15'i gecince dur
258 .toList();
259
260 System.out.println("Toplam <= 15 olana kadar alinanlar: " + alinanlar);
261 // 1+2+3+4+5 = 15 -> 5'e kadar alir, 6 eklenince 21>15 olur, durur.
262 System.out.println();
263 }
264
265 /**
266 * Elemanlari toplaya toplaya esige kadar alir; esik asildiginda akisi keser.
267 * Durum: bir int dizisi [toplam] (mutable sayac).
268 */
269 static Gatherer<Integer, ?, Integer> toplamEsigineKadar(int esik) {
270 return Gatherer.ofSequential(
271 () -> new int[]{0}, // durum: toplam
272 (toplam, eleman, downstream) -> {
273 if (toplam[0] + eleman > esik) {
274 return false; // esik asildi -> akisi durdur (short-circuit)
275 }
276 toplam[0] += eleman;
277 return downstream.push(eleman);
278 });
279 }
280}Bu sürüm-analizi örneği tek dosya olarak yerel JDK 21 ile çalıştırılamadı (bağımsız bir main içermeyebilir ya da daha yeni bir JDK / önizleme özelliği gerektirir). İlgili dil özelliği yukarıdaki anlatım ve kodda gösterilmiştir.