Takip et

Java 8 Özellikleri ve Örneklerle Detaylı Anlatım

Java 8 Özellikleri ve Örneklerle Detaylı Anlatım Java platformu, 2014 yılında yayınlanan Java 8 ile önemli bir dönüm noktasına ulaştı.

Java 8 Özellikleri ve Örneklerle Detaylı Anlatım

Java platformu, 2014 yılında yayınlanan Java 8 ile önemli bir dönüm noktasına ulaştı. Bu sürüm, dilin evriminde büyük bir sıçramayı temsil ederek, özellikle fonksiyonel programlama paradigmalarını Java dünyasına entegre etti. Geliştiricilerin kod yazma şeklini kökten değiştiren Java 8, daha temiz, daha özlü ve daha verimli kodlar yazma imkanı sundu. Bu makalede, Java 8’in getirdiği temel özellikleri, detaylı açıklamalar ve pratik örneklerle ele alacağız.

1. Lambda İfadeleri (Lambda Expressions)

Java 8’in en çığır açıcı özelliklerinden biri olan Lambda İfadeleri, fonksiyonel programlamanın temel taşlarından biridir. Tek bir soyut metodu olan arayüzlerin (yani fonksiyonel arayüzlerin) anonim olarak implementasyonunu sağlarlar. Bu sayede, daha az kodla daha işlevsel yapılar oluşturmak mümkün hale gelir.

Lambda İfadelerinin Yapısı ve Avantajları

Bir lambda ifadesi temelde üç bölümden oluşur:
1. Parametre Listesi: Metodun argümanları. Parantez içinde belirtilirler (örneğin, (a, b) veya (String s)). Parametre yoksa boş parantez () kullanılır. Tek bir parametre varsa ve tipi açıkça belirtilmiyorsa parantez kullanılmayabilir (örneğin, s ->).
2. Ok İşareti: Parametre listesi ile gövdeyi ayıran -> sembolü.
3. Gövde: Lambda ifadesinin çalıştıracağı kod bloğu. Tek bir ifade ise süslü parantez {} isteğe bağlıdır. Birden fazla ifade ise süslü parantezler zorunludur.

Avantajları:
* Daha Az Kod: Anonim iç sınıflara göre çok daha az kod yazmayı sağlar.
* Okunabilirlik: Kodun amacını daha net bir şekilde ifade eder.
* Fonksiyonel Programlama: Java’da fonksiyonel programlama stilini benimsemeyi kolaylaştırır.

Örnek: Runnable arayüzünü lambda ile kullanma

import java.util.Comparator;

public class LambdaOrnek {
    public static void main(String[] args) {
        // Geleneksel yöntemle Runnable kullanımı (anonim iç sınıf)
        Runnable eskiRunnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Eski yöntemle çalışan Runnable.");
            }
        };
        new Thread(eskiRunnable).start();

        // Lambda ifadesiyle Runnable kullanımı
        Runnable yeniRunnable = () -> System.out.println("Lambda ile çalışan Runnable.");
        new Thread(yeniRunnable).start();

        // Comparator arayüzünü lambda ile kullanma
        Comparator integerKarsilastirici = (a, b) -> a.compareTo(b);
        System.out.println("10 ve 5 karşılaştırması: " + integerKarsilastirici.compare(10, 5)); // Pozitif değer döner
        System.out.println("5 ve 10 karşılaştırması: " + integerKarsilastirici.compare(5, 10)); // Negatif değer döner
    }
}

Bu örnekte, Runnable ve Comparator gibi fonksiyonel arayüzlerin lambda ifadeleriyle ne kadar daha sade bir şekilde yazılabileceği gösterilmiştir.

2. Fonksiyonel Arayüzler (Functional Interfaces)

Lambda ifadelerinin temelini oluşturan yapılar fonksiyonel arayüzlerdir. Bir arayüzün fonksiyonel arayüz olabilmesi için yalnızca bir tane soyut metodu (Single Abstract Method – SAM) olması gerekir. Java 8 ile birlikte java.util.function paketi altında birçok hazır fonksiyonel arayüz sunulmuştur. Bu arayüzler, yaygın fonksiyonel programlama kalıplarını desteklemek için tasarlanmıştır.

Hazır Fonksiyonel Arayüzler

* Predicate: Bir T tipindeki nesneyi alır ve boolean bir değer döndürür. Koşulları kontrol etmek için kullanılır.
* boolean test(T t);
* Consumer: Bir T tipindeki nesneyi alır ve bir işlem yapar, herhangi bir değer döndürmez.
* void accept(T t);
* Function: Bir T tipindeki nesneyi alır ve onu R tipindeki bir nesneye dönüştürür.
* R apply(T t);
* Supplier: Hiçbir argüman almaz ve T tipinde bir değer döndürür. Nesne üretmek için kullanılır.
* T get();
* UnaryOperator: Function‘nin özel bir durumudur; aynı tipte bir argüman alır ve aynı tipte bir değer döndürür.
* T apply(T t);
* BinaryOperator: BiFunction‘nin özel bir durumudur; aynı tipte iki argüman alır ve aynı tipte bir değer döndürür.
* T apply(T t1, T t2);

Örnek: Çeşitli fonksiyonel arayüzlerin kullanımı

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class FunctionalInterfaceOrnek {
    public static void main(String[] args) {
        // Predicate örneği: Bir sayının çift olup olmadığını kontrol etme
        Predicate ciftSayiMi = sayi -> sayi % 2 == 0;
        System.out.println("10 çift sayı mı? " + ciftSayiMi.test(10)); // true
        System.out.println("7 çift sayı mı? " + ciftSayiMi.test(7));   // false

        // Consumer örneği: Bir mesajı ekrana yazdırma
        Consumer mesajYazdir = mesaj -> System.out.println("Mesaj: " + mesaj);
        mesajYazdir.accept("Merhaba Java 8!");

        // Function örneği: Bir String'i büyük harfe dönüştürme
        Function buyukHarfeCevir = metin -> metin.toUpperCase();
        System.out.println("Büyük harf: " + buyukHarfeCevir.apply("java 8")); // JAVA 8

        // Supplier örneği: Rastgele bir sayı üretme
        Supplier rastgeleSayiUret = () -> Math.random();
        System.out.println("Rastgele sayı: " + rastgeleSayiUret.get());
    }
}

3. Stream API

Java 8 Stream API, koleksiyonlar üzerinde fonksiyonel stil operasyonları gerçekleştirmek için güçlü ve esnek bir yol sunar. Koleksiyonlardaki verileri işlemek için deklaratif (ne yapılacağını belirten) bir yaklaşım sağlar, imperatif (nasıl yapılacağını belirten) yaklaşıma kıyasla daha okunabilir ve paralel işlemeye daha uygun kodlar yazmaya olanak tanır.

Stream API’nin Temel Bileşenleri

Bir Stream pipeline’ı genellikle şu adımlardan oluşur:
1. Kaynak (Source): Bir koleksiyon, dizi, I/O kanalı veya bir üretici fonksiyonu olabilir.
2. Ara İşlemler (Intermediate Operations): Bir stream’i başka bir stream’e dönüştüren işlemlerdir. Zincirlenebilirler (lazy evaluation) ve terminal bir işlem çağrılana kadar çalışmazlar. Örnekler: filter(), map(), distinct(), sorted(), peek(), limit(), skip().
3. Terminal İşlem (Terminal Operation): Stream’i sonlandıran ve bir sonuç üreten işlemlerdir. Stream’i tüketirler ve bir daha kullanılamaz hale getirirler. Örnekler: forEach(), collect(), reduce(), count(), min(), max(), anyMatch(), allMatch(), noneMatch(), findFirst(), findAny().

Örnek: Stream API ile veri işleme

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamApiOrnek {
    public static void main(String[] args) {
        List kelimeler = Arrays.asList("elma", "armut", "kiraz", "üzüm", "nar", "karpuz");

        // 1. 'a' harfi içeren kelimeleri filtrele
        // 2. Bu kelimeleri büyük harfe dönüştür
        // 3. Sırala
        // 4. Yeni bir List olarak topla
        List islenmisKelimeler = kelimeler.stream()
                .filter(kelime -> kelime.contains("a")) // Ara işlem: 'a' içerenleri filtrele
                .map(String::toUpperCase)              // Ara işlem: Büyük harfe çevir (Metot Referansı)
                .sorted()                              // Ara işlem: Alfabetik sırala
                .collect(Collectors.toList());         // Terminal işlem: Sonuçları listeye topla

        System.out.println("İşlenmiş kelimeler: " + islenmisKelimeler);
        // Çıktı: [ARMUT, ELMA, KARPUZ, NAR]

        // Sayı listesi üzerinde çeşitli işlemler
        List sayilar = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // Çift sayıların karelerinin toplamını bulma
        int toplamKare = sayilar.stream()
                .filter(s -> s % 2 == 0) // Sadece çift sayıları filtrele
                .map(s -> s * s)         // Karelerini al
                .reduce(0, Integer::sum); // Başlangıç değeri 0, toplama işlemi (Metot Referansı)

        System.out.println("Çift sayıların karelerinin toplamı: " + toplamKare); // Çıktı: 220 (4+16+36+64+100)

        // İlk 3 tek sayıyı bul ve ekrana yazdır
        sayilar.stream()
                .filter(s -> s % 2 != 0) // Tek sayıları filtrele
                .limit(3)                // İlk 3 tanesini al
                .forEach(System.out::println); // Her birini ekrana yazdır (Metot Referansı)
        // Çıktı:
        // 1
        // 3
        // 5

        // Paralel Stream kullanımı (büyük veri setleri için faydalıdır)
        long baslangic = System.nanoTime();
        long sayiAdedi = sayilar.parallelStream()
                                .filter(s -> s > 5)
                                .count();
        long bitis = System.nanoTime();
        System.out.println("5'ten büyük sayı adedi: " + sayiAdedi + ", Süre: " + (bitis - baslangic) + " ns");
    }
}

Stream API, büyük veri setlerini işlerken ve paralel işlemleri kolayca uygulamak istediğinizde oldukça güçlü bir araçtır.

4. Arayüzlerde Varsayılan Metotlar (Default Methods in Interfaces)

Java 8 öncesinde, bir arayüze yeni bir metot eklemek, bu arayüzü implemente eden tüm sınıfların bozulmasına neden olurdu, çünkü bu sınıfların yeni metodu implemente etmesi gerekirdi. Varsayılan metotlar, bu sorunu çözmek için tanıtıldı. default anahtar kelimesi kullanılarak, arayüzlere metot gövdeleri eklenebilir. Bu sayede, arayüz genişletilirken mevcut implementasyonlar bozulmaz.

Ayrıca, arayüzlere static metotlar da eklenebilir. Bu metotlar, arayüzün bir implementasyonuna ihtiyaç duymadan doğrudan arayüz adı üzerinden çağrılabilir.

Örnek: Varsayılan metot ve statik metot kullanımı

interface Calisan {
    void calis(); // Soyut metot

    // Varsayılan metot
    default void molaVer() {
        System.out.println("Çalışan mola veriyor (varsayılan).");
    }

    // Statik metot
    static void sirketBilgisi() {
        System.out.println("Bu şirket 1990 yılında kuruldu.");
    }
}

class Yazilimci implements Calisan {
    @Override
    public void calis() {
        System.out.println("Yazılımcı kod yazıyor.");
    }

    // Varsayılan metodu override edebilir
    @Override
    public void molaVer() {
        System.out.println("Yazılımcı kahve molası veriyor.");
    }
}

class Muhasebeci implements Calisan {
    @Override
    public void calis() {
        System.out.println("Muhasebeci defter tutuyor.");
    }
    // Muhasebeci molaVer metodunu override etmiyor, varsayılanı kullanacak
}

public class DefaultMethodOrnek {
    public static void main(String[] args) {
        Yazilimci yazilimci = new Yazilimci();
        yazilimci.calis();
        yazilimci.molaVer(); // Yazılımcı kendi override ettiği metodu çağırır

        Muhasebeci muhasebeci = new Muhasebeci();
        muhasebeci.calis();
        muhasebeci.molaVer(); // Muhasebeci varsayılan metodu çağırır

        Calisan.sirketBilgisi(); // Statik metot doğrudan arayüz üzerinden çağrılır
    }
}

5. Optional Sınıfı

java.util.Optional sınıfı, bir değerin varlığını veya yokluğunu temsil etmek için kullanılır. Temel amacı, NullPointerException (NPE) hatalarını önlemektir. Geleneksel olarak, bir metot null döndürdüğünde, çağıranın bu null durumu kontrol etmesi ve uygun şekilde ele alması gerekirdi. Optional ile, bir değerin mevcut olup olmadığını açıkça belirtirsiniz ve bu durumu daha güvenli bir şekilde yönetmenizi sağlarsınız.

Optional Kullanımının Avantajları

* NPE Koruması: null kontrolleri yerine Optional metotlarını kullanarak daha güvenli kod yazılır.
* Kod Okunabilirliği: Bir metodun Optional döndürmesi, o metodun bir değer döndürmeyebileceğini açıkça belirtir.
* Deklaratif Yaklaşım: ifPresent(), orElse(), map(), flatMap() gibi metotlar sayesinde daha fonksiyonel ve deklaratif kod yazılır.

Örnek: Optional sınıfının kullanımı

import java.util.Optional;

public class OptionalOrnek {
    public static Optional kullaniciAdiGetir(int id) {
        if (id == 1) {
            return Optional.of("ali"); // Değer mevcut
        } else if (id == 2) {
            return Optional.of("veli");
        } else {
            return Optional.empty(); // Değer mevcut değil
        }
    }

    public static void main(String[] args) {
        // ID 1 için kullanıcı adını getir
        Optional kullanici1 = kullaniciAdiGetir(1);
        kullanici1.ifPresent(ad -> System.out.println("Kullanıcı 1 Adı: " + ad)); // Eğer değer varsa yazdır

        // ID 3 için kullanıcı adını getir (mevcut değil)
        Optional kullanici3 = kullaniciAdiGetir(3);
        System.out.println("Kullanıcı 3 Adı: " + kullanici3.orElse("Misafir")); // Eğer değer yoksa "Misafir" kullan

        // Eğer değer yoksa hata fırlat
        try {
            Optional kullanici4 = kullaniciAdiGetir(4);
            String ad4 = kullanici4.orElseThrow(() -> new IllegalArgumentException("Kullanıcı bulunamadı!"));
            System.out.println("Kullanıcı 4 Adı: " + ad4);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }

        // Map metodu ile Optional içindeki değeri dönüştürme
        Optional kullanici2 = kullaniciAdiGetir(2);
        String buyukHarfAd = kullanici2.map(String::toUpperCase)
                                       .orElse("BILINMIYOR");
        System.out.println("Kullanıcı 2 Adı (Büyük Harf): " + buyukHarfAd);
    }
}

6. Yeni Date ve Time API (java.time paketi)

Java 8’den önce java.util.Date ve java.util.Calendar sınıfları, tarih ve saat işlemleri için kullanılıyordu. Ancak bu sınıfların birçok problemi vardı: değiştirilebilir olmaları, thread-safe olmamaları, API’lerinin karmaşık ve tutarsız olması gibi. Java 8, bu sorunları çözmek için Joda-Time kütüphanesinden esinlenerek tamamen yeni bir Date ve Time API’si (JSR 310) tanıttı. java.time paketi altında yer alan bu yeni API, daha temiz, daha güvenli ve daha sezgisel bir kullanım sunar.

Temel Sınıflar

* LocalDate: Yıl, ay, gün gibi tarih bilgisini tutar, saat bilgisi içermez.
* LocalTime: Saat, dakika, saniye, nanosaniye gibi saat bilgisini tutar, tarih bilgisi içermez.
* LocalDateTime: Hem tarih hem de saat bilgisini tutar, zaman dilimi bilgisi içermez.
* ZonedDateTime: LocalDateTime‘a ek olarak zaman dilimi bilgisini de tutar.
* Instant: Unix epoch’tan (1 Ocak 1970 UTC) bu yana geçen zamanı nanosaniye hassasiyetinde temsil eder. Makine zamanı için idealdir.
* Duration: İki Instant veya LocalTime arasındaki zaman farkını saniye ve nanosaniye olarak ölçer.
* Period: İki LocalDate arasındaki zaman farkını yıl, ay ve gün olarak ölçer.

Tüm bu sınıflar immutabledır (değiştirilemez), bu da onları thread-safe yapar.

Örnek: Yeni Date ve Time API kullanımı

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class DateTimeApiOrnek {
    public static void main(String[] args) {
        // LocalDate kullanımı
        LocalDate bugun = LocalDate.now();
        System.out.println("Bugünün tarihi: " + bugun); // YYYY-MM-DD
        LocalDate dogumGunu = LocalDate.of(1990, Month.MAY, 15);
        System.out.println("Doğum günü: " + dogumGunu);

        // LocalTime kullanımı
        LocalTime suAnkiSaat = LocalTime.now();
        System.out.println("Şu anki saat: " + suAnkiSaat); // HH:MM:SS.NNNNNNNNN
        LocalTime belirliSaat = LocalTime.of(14, 30, 0);
        System.out.println("Belirli saat: " + belirliSaat);

        // LocalDateTime kullanımı
        LocalDateTime suAnkiTarihSaat = LocalDateTime.now();
        System.out.println("Şu anki tarih ve saat: " + suAnkiTarihSaat);
        LocalDateTime gelecektekiTarihSaat = suAnkiTarihSaat.plusDays(5).plusHours(3);
        System.out.println("5 gün 3 saat sonra: " + gelecektekiTarihSaat);

        // ZonedDateTime kullanımı (Zaman dilimli tarih ve saat)
        ZonedDateTime newYorkZamani = ZonedDateTime.now(ZoneId.of("America/New_York"));
        System.out.println("New York zamanı: " + newYorkZamani);

        // Tarih ve saatler arası fark hesaplama (Period ve Duration)
        LocalDate tarih1 = LocalDate.of(2023, 1, 1);
        LocalDate tarih2 = LocalDate.of(2024, 5, 10);
        long gunFarki = ChronoUnit.DAYS.between(tarih1, tarih2);
        System.out.println("İki tarih arası gün farkı: " + gunFarki);

        // Tarih ve saat formatlama
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
        String formatliTarihSaat = suAnkiTarihSaat.format(formatter);
        System.out.println("Formatlı tarih ve saat: " + formatliTarihSaat);

        // String'den tarih/saat parse etme
        String tarihStr = "25/12/2023 10:00:00";
        LocalDateTime parsedDateTime = LocalDateTime.parse(tarihStr, formatter);
        System.out.println("Parse edilmiş tarih ve saat: " + parsedDateTime);
    }
}

7. Metot Referansları (Method References)

Metot referansları, lambda ifadelerinin daha kısa ve okunabilir bir alternatifidir. Bir lambda ifadesi sadece mevcut bir metodu çağırıyorsa, bu lambda ifadesini bir metot referansı ile değiştirebiliriz. Bu, kodun daha da sadeleşmesini sağlar ve kodun ne yaptığını daha net bir şekilde ifade eder.

Metot Referansı Türleri

1. Statik Metot Referansı: ClassName::staticMethodName
* Örnek: Math::max (lambda karşılığı: (a, b) -> Math.max(a, b))
2. Belirli Bir Nesnenin Örnek Metodu Referansı: object::instanceMethodName
* Örnek: System.out::println (lambda karşılığı: s -> System.out.println(s))
3. Belirli Bir Türdeki Rastgele Bir Nesnenin Örnek Metodu Referansı: ClassName::instanceMethodName
* Örnek: String::length (lambda karşılığı: s -> s.length())
* Örnek: String::compareToIgnoreCase (lambda karşılığı: (s1, s2) -> s1.compareToIgnoreCase(s2))
4. Kurucu Metot Referansı: ClassName::new
* Örnek: ArrayList::new (lambda karşılığı: () -> new ArrayList())

Örnek: Metot referanslarının kullanımı

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

public class MethodReferenceOrnek {
    public static void main(String[] args) {
        List isimler = Arrays.asList("Ayşe", "Fatma", "Can", "Deniz");

        // 1. Belirli bir nesnenin örnek metodu referansı (System.out::println)
        System.out.println("İsimler (forEach ile):");
        isimler.forEach(System.out::println);
        // Lambda karşılığı: isimler.forEach(isim -> System.out.println(isim));

        // 2. Statik metot referansı (Integer::sum)
        List sayilar = Arrays.asList(10, 20, 30, 40, 50);
        int toplam = sayilar.stream().reduce(0, Integer::sum);
        System.out.println("Sayıların toplamı: " + toplam);
        // Lambda karşılığı: sayilar.stream().reduce(0, (a, b) -> Integer.sum(a, b));

        // 3. Belirli bir türdeki rastgele bir nesnenin örnek metodu referansı (String::toUpperCase)
        List buyukHarfIsimler = isimler.stream()
                                             .map(String::toUpperCase)
                                             .collect(Collectors.toList());
        System.out.println("Büyük harf isimler: " + buyukHarfIsimler);
        // Lambda karşılığı: isimler.stream().map(isim -> isim.toUpperCase()).collect(Collectors.toList());

        // 4. Kurucu metot referansı (ArrayList::new)
        // Bu örnek doğrudan listeyi kopyalamak için kullanılmaz, ancak Stream'in collect metoduyla birleştirilebilir.
        // Örneğin, bir stream'den yeni bir ArrayList oluşturmak:
        List yeniIsimListesi = isimler.stream()
                                             .collect(Collectors.toCollection(java.util.ArrayList::new));
        System.out.println("Yeni isim listesi (kurucu referansı ile): " + yeniIsimListesi);
        // Lambda karşılığı: Collectors.toCollection(() -> new ArrayList());
    }
}

8. Nashorn JavaScript Motoru

Java 8, Java uygulamalarının içinde JavaScript kodunu çalıştırmak için yeni bir JavaScript motoru olan Nashorn’u tanıttı. Önceki Rhino motorunun yerini alan Nashorn, daha iyi performans ve ECMAScript 5.1 uyumluluğu sunar. Bu özellik, Java uygulamalarına dinamik komut dosyası çalıştırma veya yapılandırma yeteneği eklemek isteyen geliştiriciler için faydalıdır.

Örnek: Nashorn ile JavaScript çalıştırma

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class NashornOrnek {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn"); // veya "javascript"

        try {
            // JavaScript kodunu çalıştırma
            engine.eval("print('Merhaba Nashorn!');");

            // Değişken tanımlama ve kullanma
            engine.eval("var isim = 'Java';");
            engine.eval("print('Hoş geldin ' + isim + ' 8');");

            // Java objeleriyle etkileşim
            engine.put("sayi1", 10);
            engine.put("sayi2", 20);
            engine.eval("var toplam = sayi1 + sayi2;");
            System.out.println("JavaScript'ten gelen toplam: " + engine.get("toplam"));

        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }
}

9. Paralel Dizi Sıralama (Parallel Array Sorting)

Java 8, java.util.Arrays sınıfına büyük dizileri sıralamak için yeni parallelSort() metotlarını ekledi. Bu metotlar, Fork/Join çerçevesini kullanarak diziyi paralel alt dizilere böler ve bu alt dizileri ayrı ayrı işler. Bu, özellikle çok çekirdekli işlemcilerde büyük veri setleri için önemli performans iyileştirmeleri sağlayabilir.

Örnek: Arrays.parallelSort() kullanımı

import java.util.Arrays;
import java.util.Random;

public class ParallelSortOrnek {
    public static void main(String[] args) {
        int[] sayilar = new int[10000000]; // 10 milyon elemanlı bir dizi
        Random random = new Random();

        // Diziyi rastgele sayılarla doldur
        for (int i = 0; i < sayilar.length; i++) {
            sayilar[i] = random.nextInt(1000000);
        }

        // Kopyasını oluşturarak karşılaştırma yapalım
        int[] sayilarKopya = Arrays.copyOf(sayilar, sayilar.length);

        // Geleneksel sıralama
        long baslangicNormal = System.nanoTime();
        Arrays.sort(sayilar);
        long bitisNormal = System.nanoTime();
        System.out.println("Normal sıralama süresi: " + (bitisNormal - baslangicNormal) / 1_000_000.0 + " ms");

        // Paralel sıralama
        long baslangicParallel = System.nanoTime();
        Arrays.parallelSort(sayilarKopya);
        long bitisParallel = System.nanoTime();
        System.out.println("Paralel sıralama süresi: " + (bitisParallel - baslangicParallel) / 1_000_000.0 + " ms");

        // Dizinin sıralanıp sıralanmadığını kontrol etmek için (ilk birkaç eleman)
        // System.out.println("Sıralanmış dizinin ilk 10 elemanı: " + Arrays.toString(Arrays.copyOfRange(sayilar, 0, 10)));
        // System.out.println("Paralel sıralanmış dizinin ilk 10 elemanı: " + Arrays.toString(Arrays.copyOfRange(sayilarKopya, 0, 10)));
    }
}

Bu örnek, büyük bir dizi üzerinde Arrays.sort() ile Arrays.parallelSort() arasındaki performans farkını göstermektedir. Performans farkı, sistemin çekirdek sayısına ve dizinin boyutuna göre değişebilir.

Sonuç

Java 8, dilin gelişiminde devrim niteliğinde bir adımdı. Lambda ifadeleri, Stream API, fonksiyonel arayüzler ve metot referansları gibi özellikler, Java'ya fonksiyonel programlama yeteneklerini getirerek geliştiricilere daha özlü, okunabilir ve verimli kod yazma imkanı sundu. Optional sınıfı NullPointerException riskini azaltırken, yeni Date ve Time API'si tarih ve saat işlemlerini çok daha kolay ve güvenli hale getirdi. Arayüzlerdeki varsayılan metotlar, geriye dönük uyumluluğu korurken arayüzlerin evrimini kolaylaştırdı. Nashorn motoru ve paralel dizi sıralama gibi diğer iyileştirmeler de platformun genel yeteneklerini artırdı.

Java 8'in getirdiği bu yenilikler, modern Java uygulamalarının geliştirilmesinde temel taşlar haline gelmiş ve dilin gelecekteki evrimine yön veren önemli bir temel oluşturmuştur. Her Java geliştiricisinin bu özelliklere hakim olması, daha kaliteli ve sürdürülebilir yazılımlar üretmek için kritik öneme sahiptir.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.