Gereksinimler ve Ön Hazırlık
Bu makaledeki örnekleri uygulamak için bilgisayarınızda en az Java 17 veya daha güncel bir sürümün yüklü olması önerilir. Java'nın güncel sürümleri, Stream API performans iyileştirmeleri ve yeni metotlar içermektedir.
- JDK (Java Development Kit) 17 veya üzeri.
- IDE (IntelliJ IDEA, Eclipse veya VS Code).
- Temel Java koleksiyonları (List, Set, Map) bilgisi.
Projenizi oluştururken Maven veya Gradle üzerinden projenize Java sürümünü tanımladığınızdan emin olun. Tüm örnekler standart Java kütüphaneleri ile çalışmaktadır, ekstra bir bağımlılığa ihtiyaç duymazsınız.
Stream API ile Temel Filtreleme: filter() Metodu
Stream API'nin en temel filtreleme yöntemi filter() metodudur. Bu metot, bir Predicate (mantıksal bir koşul döndüren fonksiyonel arayüz) bekler. Koşulun true döndüğü elemanlar akışta kalmaya devam eder.
import java.util.List;
import java.util.stream.Collectors;
public class TemelFiltreleme {
public static void main(String[] args) {
List sayilar = List.of(1, 10, 15, 22, 30, 45);
// Sadece 20'den büyük olan sayıları filtrele
List filtrelenmis = sayilar.stream()
.filter(sayi -> sayi > 20)
.collect(Collectors.toList());
System.out.println(filtrelenmis); // Çıktı: [22, 30, 45]
}
}
Yukarıdaki örnekte, stream() metodu ile liste bir akışa dönüştürülür. filter() içerisine yazılan lambda ifadesi, her bir elemanı kontrol eder. Eğer eleman 20'den büyükse akışta tutulur, değilse elenir.
Nesne Listelerinde Filtreleme Adımları
Gerçek dünya uygulamalarında genellikle basit sayılarla değil, özel nesnelerle (POJO - Plain Old Java Object) çalışırız. Bir "Kullanıcı" veya "Ürün" listesini filtrelemek, Stream API'nin gücünü en iyi gösterdiği alandır.
import java.util.List;
record Urun(String isim, double fiyat) {}
public class NesneFiltreleme {
public static void main(String[] args) {
List urunler = List.of(
new Urun("Laptop", 25000.0),
new Urun("Mouse", 500.0),
new Urun("Klavye", 1200.0)
);
// Fiyatı 1000 TL'den büyük olanları bul
urunler.stream()
.filter(u -> u.fiyat() > 1000.0)
.forEach(u -> System.out.println(u.isim()));
}
}
Burada record yapısını kullanarak verimizi tanımladık. filter() metodu içerisinde nesnenin fiyat özelliğine erişerek kolayca bir filtreleme gerçekleştirdik. forEach ile de sadece filtrelenen sonuçları konsola yazdırdık.
Birden Fazla Koşul ile Filtreleme
Bazen bir veriyi filtrelerken birden fazla kriteri aynı anda sağlamanız gerekir. Stream API'de filter() metodunu zincirleyerek veya mantıksal operatörler (&&, ||) kullanarak bu işlemi yapabilirsiniz.
import java.util.List;
public class CokluKriter {
public static void main(String[] args) {
List isimler = List.of("Ahmet", "Ayşe", "Mehmet", "Ali", "Can");
// Hem 'A' ile başlayan hem de uzunluğu 3'ten büyük olanlar
isimler.stream()
.filter(isim -> isim.startsWith("A"))
.filter(isim -> isim.length() > 3)
.forEach(System.out::println);
}
}
Burada iki farklı filter() kullanımı görüyoruz. İlki ismi 'A' ile başlayanları, ikincisi ise uzunluğu 3'ten büyük olanları seçer. Bu zincirleme yapısı, kodun okunabilirliğini artırır ve karmaşık if-else bloklarından kaçınmanızı sağlar.
Stream API Filtreleme Yöntemleri Karşılaştırması
| Yöntem | Avantajı | Dezavantajı |
|---|---|---|
| filter() | Okunabilir, deklaratif | Büyük veri setlerinde maliyetli olabilir |
| Geleneksel Loop | Düşük seviyeli kontrol | Kod karmaşıklığı, hata payı yüksek |
| ParallelStream | Çok çekirdekli işlem gücü | Thread-safety (iş parçacığı güvenliği) riski |
Kritik Uyarılar ve Güvenlik
Dikkat: Stream API kullanırken
nulldeğerlerine karşı dikkatli olunmalıdır. Filtreleme aşamasındanullbir nesne üzerinde metod çağırmakNullPointerExceptionhatasına yol açar. Filtreleme zincirinizin başına her zamanfilter(Objects::nonNull)ekleyerek güvenli bir başlangıç yapabilirsiniz. Ayrıca, veritabanından gelen verileri filtrelerken SQL injection riskini unutmayın; Stream API sadece uygulama katmanında çalışır, veritabanı sorgularınızda her zaman Prepared Statement kullanın.
Performans İpuçları ve Yaygın Hatalar
Yaygın bir hata, filtrelenmiş bir akışı tekrar tekrar kullanmaya çalışmaktır. Unutmayın, bir Stream nesnesi bir kez tüketildiğinde (terminal operasyonu çalıştırıldığında) tekrar kullanılamaz.
// Hatalı Kullanım
var stream = liste.stream().filter(x -> x > 5);
stream.collect(Collectors.toList());
stream.count(); // HATA: Stream kapandı!
Performans için parallelStream() kullanırken dikkatli olun. Eğer filtreleme işleminiz çok hafif bir işlemse (örneğin basit bir sayı karşılaştırması), paralel akış kullanmak ek yük (overhead) nedeniyle normal akıştan daha yavaş çalışabilir. Sadece çok büyük veri setlerinde ve ağır işlemlerde paralelleştirmeyi tercih edin.
Sıkça Sorulan Sorular
Stream API ile filtreleme yaparken orijinal liste değişir mi?
Hayır, Stream API orijinal koleksiyon üzerinde herhangi bir değişiklik yapmaz. Filtreleme işlemi sonucunda her zaman yeni bir sonuç kümesi (veya akış) oluşturulur.
Filtreleme sonucunu tekrar Listeye nasıl çeviririm?
collect(Collectors.toList()) veya Java 16 ve sonrası için daha kısa olan toList() metodunu kullanabilirsiniz.
Filtreleme kriterini dışarıdan bir değişkene bağlayabilir miyim?
Evet, bir Predicate nesnesi oluşturup bunu filter() metoduna parametre olarak geçebilirsiniz. Bu, kodunuzu daha modüler hale getirir.
Filtreleme yaparken hata ayıklama (debug) nasıl yapılır?
Stream zincirinin arasına peek(System.out::println) ekleyerek akıştan geçen elemanları anlık olarak izleyebilir ve hangi aşamada elendiklerini görebilirsiniz.
Sonsuz akışlarda filtreleme yapabilir miyim?
Evet, Stream.iterate gibi yöntemlerle oluşturulan sonsuz akışlarda limit() metodu ile bir sınır koyarak filtreleme yapabilirsiniz. Aksi takdirde programınız sonsuz döngüye girer.
Stream API ile Karmaşık Veri Gruplama ve Filtreleme Senaryosu
Gerçek dünya projelerinde, verileri sadece filtrelemek genellikle yeterli olmaz; filtrelenmiş verileri belirli kriterlere göre gruplandırmak veya özetlemek gerekebilir. Örneğin, bir e-ticaret uygulamasında belirli bir fiyat aralığındaki ürünleri filtreleyip, bu ürünleri kategorilerine göre gruplandırmak yaygın bir ihtiyaçtır. Stream API'nin Collectors.groupingBy() metodu ile filter() metodunu birleştirerek bu işlemi oldukça temiz bir şekilde gerçekleştirebilirsiniz.
Aşağıdaki örnekte, bir ürün listesini önce fiyat kriterine göre filtreliyor, ardından bu sonuçları kategori bazlı bir haritaya (Map) dönüştürüyoruz:
import java.util.*;
import java.util.stream.Collectors;
public class ProductFilterExample {
public static void main(String[] args) {
List products = Arrays.asList(
new Product("Laptop", "Elektronik", 15000),
new Product("Mouse", "Elektronik", 500),
new Product("Kitap", "Kırtasiye", 150),
new Product("Klavye", "Elektronik", 800)
);
// Fiyatı 700 TL'den büyük olanları kategoriye göre grupla
Map filteredProducts = products.stream()
.filter(p -> p.getPrice() > 700)
.collect(Collectors.groupingBy(Product::getCategory));
filteredProducts.forEach((category, list) -> {
System.out.println(category + ": " + list);
});
}
}
Stream API Filtreleme İşlemlerinde Birim Testi (Unit Testing)
Yazdığınız filtreleme mantığının doğruluğundan emin olmak için JUnit gibi kütüphanelerle birim testleri yazmak kritik öneme sahiptir. Özellikle karmaşık koşullu filtrelemelerde, beklenen sonuç ile dönen sonucu karşılaştırmak, uygulamanızın güvenilirliğini artırır.
Aşağıda, bir filtreleme metodunun doğruluğunu test eden basit bir senaryo yer almaktadır:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.util.List;
class ProductServiceTest {
@Test
void testFilterByPrice() {
List products = List.of(new Product("A", "X", 100), new Product("B", "X", 200));
List result = products.stream()
.filter(p -> p.getPrice() > 150)
.toList();
assertEquals(1, result.size());
assertEquals("B", result.get(0).getName());
}
}
Test Edilebilirlik İçin İpuçları
- Lambda İfadelerini Ayırın: Karmaşık filtreleme mantıklarını doğrudan
filter()içine yazmak yerine, ayrı bir metod veyaPredicatenesnesi olarak tanımlayın. Bu, test edilebilirliği artırır. - Sınır Değerlerini Kontrol Edin:
filter()içerisinde kullandığınız>veya>=gibi operatörlerin sınır değerlerini mutlaka test senaryolarınıza dahil edin. - Boş Liste Durumu: Filtreleme sonucunda listenin boş dönmesi durumunda uygulamanızın hata verip vermediğini (NullPointerException vb.) kontrol edin.
Sonuç
Java ile Stream API kullanarak veri filtreleme işlemi, modern yazılım geliştirmenin vazgeçilmez bir parçasıdır. Bu rehberde temel filter() kullanımından nesne odaklı filtrelemeye, zincirleme koşullardan performans ipuçlarına kadar birçok konuyu ele aldık. Bir sonraki adımınız, map() ve flatMap() gibi dönüştürme operatörlerini öğrenerek veriyi sadece filtrelemekle kalmayıp, aynı zamanda biçimlendirmeyi de öğrenmek olmalıdır. Yazılım geliştirme sürecinizde bu teknikleri bolca pratik ederek akıcı bir kod yazma alışkanlığı kazanabilirsiniz.
Not: Bu makaledeki kod örnekleri eğitim amaçlıdır ve üretim ortamında kullanılmadan önce gerekli birim testleri (unit tests) ile doğrulanmalıdır.


Yorumlar (0)
Yorum Yaz