Java 25 Ekosisteminde Guava Kütüphanesi: Kapsamlı Rehber
İçindekiler
- Giriş
- Kurulum
- Immutable (Değiştirilemez) Koleksiyonlar
- Yeni Koleksiyon Tipleri
- Caching (Önbellekleme)
- Concurrency (Eşzamanlılık) Araçları
- String İşlemleri
- I/O Yardımcıları
- Preconditions (Ön Koşullar)
- Fonksiyonel İdiomlar: Optional, Predicate, Function
- Matematik Yardımcıları
- Hashing
- EventBus
- Graph (Grafik) API’si
- Sıralama: Ordering ve ComparisonChain
- Guava ile Java 25’in Yeni Özellikleri
- Guava mı, Java Standart Kütüphanesi mi?
- En İyi Uygulamalar ve Tuzaklar
- Kaynaklar
Giriş
Guava, Google tarafından geliştirilen ve Java dünyasında onlarca yıldır kullanılan bir “çekirdek kütüphaneler” setidir (com.google.guava:guava). İçerisinde koleksiyonlar, önbellekleme, eşzamanlılık yardımcıları, string işlemleri, I/O yardımcıları, hashing, grafik yapıları ve daha fazlası bulunur.
Java zamanla java.util.Optional, List.of(), Map.of() gibi Guava’dan ilham alan özellikler kazandı; fakat Guava hâlâ birçok senaryoda daha zengin ve olgun bir API sunar. Java 25 (LTS, Eylül 2025’te yayımlandı) ile birlikte gelen sanal thread’ler (virtual threads), pattern matching, record’lar ve yapılandırılmış eşzamanlılık (structured concurrency) gibi özellikler Guava ile birlikte gayet uyumlu çalışır; Guava, JDK’nın sunmadığı tamamlayıcı araçları sağlamaya devam eder.
Güncel sürüm: Bu rehberin yazıldığı tarih itibarıyla Guava’nın güncel sürümü 33.6.0-jre‘dir (JRE varyantı; Android için -android varyantı mevcuttur). Guava, Java 8 ve üzerini destekler, dolayısıyla Java 25 ile tam uyumludur.
Not: Guava sürüm numaraları sık güncellenir; projenizde her zaman Maven Central üzerinden en güncel sürümü kontrol edin.
Kurulum
Maven
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.6.0-jre</version>
</dependency>
Gradle (Kotlin DSL)
dependencies {
// Sadece implementasyonda kullanılacaksa:
implementation("com.google.guava:guava:33.6.0-jre")
// Guava tiplerini public API'de kullanacaksanız:
api("com.google.guava:guava:33.6.0-jre")
}
Java 25 modül sistemi (JPMS) ile kullanım
Guava 33.4.8 ve sonrası, bir Java modülü olarak derlenmiştir. module-info.java içerisinde şu şekilde tanımlayabilirsiniz:
module com.benim.uygulamam {
requires com.google.common;
}
Immutable (Değiştirilemez) Koleksiyonlar
Java’nın kendi List.of(), Map.of() metotları Guava’nın ImmutableList, ImmutableMap ailesinden ilham almıştır, ancak Guava daha zengin builder API’leri ve daha fazla esneklik sunar.
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
// Basit oluşturma
ImmutableList<String> renkler = ImmutableList.of("kırmızı", "yeşil", "mavi");
// Builder ile
ImmutableMap<String, Integer> yaslar = ImmutableMap.<String, Integer>builder()
.put("Ayşe", 30)
.put("Mehmet", 25)
.buildOrThrow(); // 33.x'te build() yerine buildOrThrow() önerilir
// Mevcut bir koleksiyondan kopyalama
List<String> kaynak = new ArrayList<>(List.of("a", "b", "c"));
ImmutableSet<String> kopya = ImmutableSet.copyOf(kaynak);
// Stream ile toplama
ImmutableList<Integer> kareler = Stream.of(1, 2, 3, 4)
.map(x -> x * x)
.collect(ImmutableList.toImmutableList());
Neden ImmutableList/Map kullanmalı?
- Thread-safe’dir (paylaşılan durum için güvenlidir).
nullelemanlara izin vermez, bu da hataları erken yakalamayı sağlar.- Bellek açısından
Collections.unmodifiableList(...)sarmalamalarından daha verimlidir. - Java 25’teki sanal thread’lerle yoğun eşzamanlı kodda paylaşılan sabit veriler için idealdir.
Yeni Koleksiyon Tipleri
Guava, JDK’da bulunmayan birçok koleksiyon tipi sunar:
Multimap - Bir anahtara birden fazla değer
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
Multimap<String, String> dersOgrenciler = ArrayListMultimap.create();
dersOgrenciler.put("Matematik", "Ali");
dersOgrenciler.put("Matematik", "Veli");
dersOgrenciler.put("Fizik", "Ayşe");
Collection<String> matematikOgrencileri = dersOgrenciler.get("Matematik");
// [Ali, Veli]
Multiset - Eleman sayısını tutan koleksiyon
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
Multiset<String> kelimeSayaci = HashMultiset.create();
for (String kelime : "elma armut elma muz elma armut".split(" ")) {
kelimeSayaci.add(kelime);
}
System.out.println(kelimeSayaci.count("elma")); // 3
BiMap - İki yönlü eşleme
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
BiMap<String, Integer> isimSira = HashBiMap.create();
isimSira.put("Ali", 1);
isimSira.put("Veli", 2);
Integer sira = isimSira.get("Ali"); // 1
String isim = isimSira.inverse().get(2); // Veli
Table - İki boyutlu anahtar-değer yapısı
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
Table<String, String, Double> notlar = HashBasedTable.create();
notlar.put("Ali", "Matematik", 85.0);
notlar.put("Ali", "Fizik", 90.0);
notlar.put("Veli", "Matematik", 70.0);
Double aliMatematik = notlar.get("Ali", "Matematik"); // 85.0
Map<String, Double> aliTumNotlari = notlar.row("Ali");
RangeSet / RangeMap - Aralık tabanlı veri yapıları
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
RangeSet<Integer> araliklar = TreeRangeSet.create();
araliklar.add(Range.closed(1, 5));
araliklar.add(Range.closed(10, 15));
araliklar.add(Range.closed(4, 11)); // birleşir -> [1, 15]
boolean icindeMi = araliklar.contains(7); // true
Caching (Önbellekleme)
Guava’nın LoadingCache yapısı, Caffeine yaygınlaşmadan önce Java’da en çok tercih edilen bellek içi önbellekleme çözümüydü ve hâlâ birçok eski/yeni projede aktif kullanılır.
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.TimeUnit;
LoadingCache<String, ExpensiveObject> onbellek = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.expireAfterAccess(5, TimeUnit.MINUTES)
.recordStats()
.build(new CacheLoader<String, ExpensiveObject>() {
@Override
public ExpensiveObject load(String anahtar) {
return maliyetliHesapla(anahtar);
}
});
ExpensiveObject deger = onbellek.getUnchecked("anahtar1");
System.out.println(onbellek.stats()); // isabet oranı, kaçırma sayısı vb.
Java 25 ile ilişkisi: Sanal thread’lerle yüksek eşzamanlı I/O senaryolarında LoadingCache‘in synchronized bloklarına dayalı bazı iç mekanizmaları thread pinning’e yol açabilir. Yoğun sanal thread kullanımı olan projelerde (özellikle expireAfterAccess gibi zaman damgası güncellemesi gerektiren durumlarda), Caffeine kütüphanesine geçiş değerlendirilebilir; ancak orta ölçekli, senkron çalışan uygulamalarda Guava Cache hâlâ gayet performanslıdır.
Concurrency (Eşzamanlılık) Araçları
ListenableFuture ve Futures
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import java.util.concurrent.Executors;
ListeningExecutorService havuz =
MoreExecutors.listeningDecorator(Executors.newVirtualThreadPerTaskExecutor());
ListenableFuture<String> gelecek = havuz.submit(() -> {
Thread.sleep(100);
return "sonuç";
});
Futures.addCallback(gelecek, new FutureCallback<String>() {
@Override
public void onSuccess(String sonuc) {
System.out.println("Başarılı: " + sonuc);
}
@Override
public void onFailure(Throwable hata) {
System.err.println("Hata: " + hata.getMessage());
}
}, havuz);
Dikkat: Java 25’te
Executors.newVirtualThreadPerTaskExecutor()ileMoreExecutors.listeningDecorator(...)gayet uyumlu çalışır; Guava’nınListenableFuture‘ı,CompletableFuture‘dan önceki dönemde geliştirilmiş olsa da callback tabanlı zincirleme senaryolarında hâlâ tercih edilebilir bir API sunar.
RateLimiter - Hız sınırlama
import com.google.common.util.concurrent.RateLimiter;
RateLimiter limiter = RateLimiter.create(5.0); // saniyede 5 istek
for (int i = 0; i < 10; i++) {
limiter.acquire(); // gerekirse bekler
apiCagrisiYap();
}
Striped - Anahtar bazlı kilitler
import com.google.common.util.concurrent.Striped;
import java.util.concurrent.locks.Lock;
Striped<Lock> kilitler = Striped.lock(16);
Lock kilit = kilitler.get(kullaniciId);
kilit.lock();
try {
kullaniciBakiyesiGuncelle(kullaniciId);
} finally {
kilit.unlock();
}
Monitor - synchronized’a modern alternatif
import com.google.common.util.concurrent.Monitor;
Monitor monitor = new Monitor();
monitor.enter();
try {
// kritik bölge
} finally {
monitor.leave();
}
String İşlemleri
Joiner ve Splitter
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
String birlesik = Joiner.on(", ").skipNulls().join("a", null, "b", "c");
// "a, b, c"
List<String> parcalar = Splitter.on(",")
.trimResults()
.omitEmptyStrings()
.splitToList(" elma, , armut ,muz");
// [elma, armut, muz]
CharMatcher
import com.google.common.base.CharMatcher;
String temiz = CharMatcher.whitespace().trimFrom(" merhaba dünya ");
String sadeceRakamlar = CharMatcher.inRange('0', '9').retainFrom("Ürün-Kodu-4521-AX");
// "4521"
Strings sınıfı
import com.google.common.base.Strings;
String bosSaKontrol = Strings.nullToEmpty(null); // ""
boolean bosMu = Strings.isNullOrEmpty(""); // true
String doldurulmus = Strings.padStart("42", 5, '0'); // "00042"
I/O Yardımcıları
import com.google.common.io.Files;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import java.io.File;
import java.nio.charset.StandardCharsets;
// Dosya içeriğini okuma
String icerik = Files.asCharSource(new File("veri.txt"), StandardCharsets.UTF_8).read();
// Dosyaya yazma
Files.asCharSink(new File("cikti.txt"), StandardCharsets.UTF_8).write("merhaba dünya");
// Bir dizini gezme
Files.fileTraverser().depthFirstPreOrder(new File("./proje"))
.forEach(dosya -> System.out.println(dosya.getName()));
Not: Java 25’te
java.nio.file.Filesçoğu senaryo için yeterlidir (Files.readString,Files.writeStringgibi metotlarla); Guava’nın I/O paketi iseByteSource/CharSourcegibi kompozisyona uygun soyutlamalarıyla daha karmaşık veri akışı senaryolarında değerli olmaya devam eder.
Preconditions (Ön Koşullar)
import static com.google.common.base.Preconditions.*;
public void kullaniciOlustur(String isim, int yas) {
checkNotNull(isim, "İsim boş olamaz");
checkArgument(yas >= 0, "Yaş negatif olamaz: %s", yas);
checkState(veritabaniBagliMi(), "Veritabanı bağlantısı yok");
// ...
}
Java 25’in pattern matching ve record’larıyla birlikte kullanıldığında, Preconditions sınıfı özellikle record kurucularında (compact constructor) doğrulama yapmak için hâlâ pratik bir seçenektir:
public record Kullanici(String isim, int yas) {
public Kullanici {
checkArgument(yas >= 0, "Yaş negatif olamaz: %s", yas);
checkNotNull(isim);
}
}
Fonksiyonel İdiomlar
Guava Optional vs java.util.Optional
Java 8 öncesinde Guava’nın kendi com.google.common.base.Optional sınıfı vardı. Günümüzde yeni kodda java.util.Optional tercih edilmelidir; Guava’nın Optional‘ı ise eski API’lerle uyum için hâlâ mevcuttur ve iki tür arasında dönüşüm sağlanabilir:
import com.google.common.base.Optional;
Optional<String> guavaOpt = Optional.of("değer");
java.util.Optional<String> javaOpt = guavaOpt.toJavaUtil();
Predicates ve Functional Idioms
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
Predicate<String> uzunMu = s -> s.length() > 5;
Collection<String> uzunlar = Collections2.filter(List.of("kısa", "oldukça uzun kelime"), uzunMu);
Not: Java 8’in
java.util.function.Predicate‘i ve Stream API’si, günlük filtreleme/dönüştürme işlerinde artık birinci tercih olmalıdır. Guava’nın fonksiyonel yardımcıları, eski API’lerle (örneğinMultimaps.filterValues) entegrasyon gerektiğinde önem taşır.
Matematik Yardımcıları
import com.google.common.math.IntMath;
import com.google.common.math.LongMath;
import com.google.common.math.DoubleMath;
int ebob = IntMath.gcd(48, 18); // 6
boolean asalMi = IntMath.isPrime(17); // true
int tavan = IntMath.divide(7, 2, java.math.RoundingMode.CEILING); // 4
long faktoriyel = LongMath.factorial(10);
boolean yaklasikEsit = DoubleMath.fuzzyEquals(0.1 + 0.2, 0.3, 1e-9); // true
Hashing
import com.google.common.hash.Hashing;
import com.google.common.hash.HashFunction;
import com.google.common.hash.HashCode;
HashFunction hf = Hashing.sha256();
HashCode hash = hf.hashString("merhaba dünya", StandardCharsets.UTF_8);
System.out.println(hash.toString());
// Bloom Filter
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
BloomFilter<String> filtre = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), 1_000_000, 0.01);
filtre.put("kullanici123");
boolean muhtemelenVar = filtre.mightContain("kullanici123"); // true (yanlış pozitif olabilir, yanlış negatif olamaz)
EventBus
Guava’nın EventBus‘ı basit, uygulama içi yayın/abone (pub-sub) modeli sunar:
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
class SiparisDinleyici {
@Subscribe
public void siparisOlusturuldu(SiparisOlayi olay) {
System.out.println("Yeni sipariş: " + olay.getSiparisId());
}
}
EventBus veriYolu = new EventBus();
veriYolu.register(new SiparisDinleyici());
veriYolu.post(new SiparisOlayi("SIP-1001"));
Not: Yeni projelerde reaktif akışlar veya Java 25’in yapılandırılmış eşzamanlılığı (structured concurrency,
StructuredTaskScope) tercih edilebilir; ancak basit, tek JVM içi olay yönetimi içinEventBushâlâ hafif ve kullanışlı bir seçenektir.
Graph (Grafik) API’si
com.google.common.graph paketi, yönlü/yönsüz grafik yapıları modellemek için kullanılır:
import com.google.common.graph.GraphBuilder;
import com.google.common.graph.MutableGraph;
MutableGraph<String> sehirGrafigi = GraphBuilder.undirected().build();
sehirGrafigi.putEdge("İstanbul", "Ankara");
sehirGrafigi.putEdge("Ankara", "İzmir");
Set<String> komsular = sehirGrafigi.adjacentNodes("Ankara");
// [İstanbul, İzmir]
Sıralama: Ordering ve ComparisonChain
import com.google.common.collect.Ordering;
import com.google.common.collect.ComparisonChain;
Ordering<String> uzunlugaGore = Ordering.natural().onResultOf(String::length);
List<String> siraliListe = uzunlugaGore.sortedCopy(List.of("uzun kelime", "az", "orta"));
public int compareTo(Kullanici diger) {
return ComparisonChain.start()
.compare(this.soyad, diger.soyad)
.compare(this.ad, diger.ad)
.compare(this.yas, diger.yas)
.result();
}
Guava ile Java 25’in Yeni Özellikleri
Java 25 (LTS), önceki sürümlerden gelen birçok özelliği kalıcı hale getirdi ve yeni önizlemeler ekledi. Guava ile etkileşimleri şu şekilde özetlenebilir:
| Java 25 Özelliği | Guava ile İlişkisi |
|---|---|
| Sanal Thread’ler (Virtual Threads) | MoreExecutors.listeningDecorator() ile Executors.newVirtualThreadPerTaskExecutor() birlikte kullanılabilir. Striped kilitleri ve synchronized bloklu eski Guava API’leri sanal thread pinning riski taşıyabilir; kritik yollarda ReentrantLock tabanlı alternatifler tercih edilebilir. |
| Record’lar | Preconditions ile compact constructor doğrulamaları; ImmutableMap/ImmutableList record alanlarında değişmezliği pekiştirmek için idealdir. |
| Pattern Matching (switch ifadeleri) | Guava’nın Range, Optional gibi tipleriyle birlikte okunabilir switch blokları yazılabilir. |
| Yapılandırılmış Eşzamanlılık (Structured Concurrency) | StructuredTaskScope, Guava’nın ListenableFuture tabanlı callback modeline göre daha modern bir alternatiftir; yeni kodda structured concurrency, eski kodda ListenableFuture bir arada bulunabilir. |
| Modül Sistemi (JPMS) | Guava 33.4.8+ artık gerçek bir Java modülü; module-info.java ile temiz entegrasyon sağlanır. |
Guava mı, Java Standart Kütüphanesi mi?
| İhtiyaç | Önerilen |
|---|---|
| Basit değişmez liste/map/set | Java List.of()/Map.of() (basit durumlar) veya Guava ImmutableList (builder, zengin API gerektiğinde) |
| Multimap, Multiset, BiMap, Table | Guava (JDK’da karşılığı yok) |
| Bellek içi önbellekleme | Guava LoadingCache veya Caffeine (yüksek performans/sanal thread yoğun senaryolar için) |
| Optional | java.util.Optional (yeni kod için) |
| String birleştirme/bölme | Guava Joiner/Splitter (zengin seçenekler) veya String.join/split (basit durumlar) |
| Asenkron programlama | Java CompletableFuture / sanal thread’ler (yeni kod) veya Guava ListenableFuture (eski kod tabanı, callback zincirleme) |
| Hashing / Bloom Filter | Guava (JDK’da Bloom Filter yok) |
| Grafik veri yapıları | Guava common.graph |
En İyi Uygulamalar ve Tuzaklar
- @Beta API’lerden kaçının: Guava’da
@Betaişaretli sınıflar herhangi bir zamanda değişebilir veya kaldırılabilir. Kütüphane geliştiriyorsanız Guava Beta Checker kullanın. - buildOrThrow() kullanın:
ImmutableMap.Builder.build()yerine, çakışan anahtarlarda daha net hata mesajı verenbuildOrThrow()tercih edilmelidir. - Modül geçişine dikkat: 33.4.5–33.4.7 sürümlerinde modülerleştirme ile ilgili bilinen sorunlar vardı; doğrudan 33.4.8 veya sonrası kullanın.
- Sanal thread’lerle senkronizasyon:
synchronizedblok içeren eski Guava API’lerini yoğun sanal thread kullanılan kod yollarında dikkatli kullanın (thread pinning riski). - Gereksiz bağımlılık şişkinliğinden kaçının: Sadece birkaç yardımcı metot için tüm Guava’yı eklemek yerine, gerçekten ihtiyaç olup olmadığını değerlendirin; modern JDK birçok temel ihtiyacı zaten karşılıyor.
- Nullness açıklamaları: Yeni Guava sürümleri JSpecify null-safety açıklamalarına geçiş yapıyor; Kotlin ile birlikte kullanıyorsanız derleme uyarılarına dikkat edin.
Kaynaklar
- Guava resmi GitHub deposu: https://github.com/google/guava
- Guava kullanıcı kılavuzu (wiki): https://github.com/google/guava/wiki
- Maven Central üzerinde güncel sürüm: https://mvnrepository.com/artifact/com.google.guava/guava
- Guava Javadoc: https://guava.dev/
Bu belge, Guava 33.6.0-jre sürümü ve Java 25 (LTS) baz alınarak hazırlanmıştır. Kütüphane sürümleri sık güncellendiğinden, üretim projelerinde her zaman güncel sürüm notlarını kontrol ediniz.