5. Java 8'deki Stream<T> türü
5.1. Örnek-01 - Stream sınıfı
Observable akışları üzerindeki işlemler, Stream akışlarıyla birçok ortak noktaya sahiptir. Aradaki farklardan biri, Stream akışındaki bir öğenin, Stream akışının tamamı elde edilmeden işlenememesi; buna karşın, Observable akışındaki bir öğe, (gözlemlenebilir). Bir diğer fark ise, Stream akışı elde edildiğinde, değerlerinin Stream akışından tek tek çekilmesi (pull) yoluyla kullanılmasıdır. Gözlemlenebilir için durum farklıdır. Gözlemlenebilir bir değer yayınladığında, bu değer abonesine itilir (pushed).
Stream kavramını uygulayan birçok sınıf vardır. Burada Stream<T> sınıfını tanıtıyoruz:

Stream sınıfı 39 yöntemden oluşur. Bunlardan birkaçını tanıtacağız. Aşağıdaki kodu ele alalım:
![]() |
package dvp.java8.streams;
import java.util.List;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple01 {
public static void main(String[] args) {
// kişi listesi
List<Personne> personnes = Personnes.get();
// görüntüleme 1
personnes.stream().forEach(p -> {
System.out.println(p);
});
System.out.println("----------------");
// görüntüleme 2
personnes.stream().forEach(System.out::println);
}
}
- 11. satır: bir kişi listesi oluşturulur;
- 13. satır: bu listeden bir Stream oluşturulur. Tüm koleksiyonlar bu şekilde Stream akışlarına dönüştürülebilir. Bu, koleksiyondaki öğeleri döngülerle işlenmesinden daha özlü bir şekilde işlemek için bu sınıfın tüm yöntemlerinden yararlanmaya olanak tanır. Ayrıca, mümkün olduğunda öğelerin işlenmesinde paralellikten de yararlanmaya olanak tanır;
- 13. satır: [Stream.forEach] yönteminin imzası şöyledir:
![]() |
Görüldüğü gibi, yöntemin parametresi 4.4. paragrafta tanıtılan [Consumer<T>] işlevsel arayüzüdür; bu arayüzün tek yöntemi T türünü kullanır ve hiçbir değer döndürmez.
- Kodda:
personnes.stream().forEach(p -> {
System.out.println(p);
});
- [personnes.stream()], [forEach] yöntemine veri sağlayan [Personne] türünde bir öğe akışı üretir. p parametresi [Personne] türündedir ve sağlanan lambda fonksiyonu bu kişiyi görüntüler;
Yukarıdaki kod şu şekilde basitleştirilebilir (18. satır):
personnes.stream().forEach(System.out::println);
Bir lambda fonksiyonunun değerini parametre olarak aktarmak yerine, mevcut bir yöntemin referansını aktarıyoruz; burada System.out sınıfının println yöntemi. Elbette bu yöntemin doğru imza yapısına sahip olması gerekir; burada [Consumer.accept] yönteminin imzası: void accept(T t). Daha önce de belirtildiği gibi, [accept] yönteminin parametresi [Personne] türü olacaktır;
Şu sonuçları elde ediyoruz:
Bir Stream akışı kullanıldıktan sonra, artık kullanılamaz hale gelir. Tekrar kullanmak istiyorsak, onu yeniden oluşturmamız gerekir. Bu durum, aşağıdaki [Exemple01b] kodunda gösterilmiştir:
package dvp.java8.streams;
import java.util.stream.Stream;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple01b {
public static void main(String[] args) {
// kişi akışı
Stream<Personne> personnes = Personnes.get().stream();
// görüntüleme 1
personnes.forEach(p -> {
System.out.println(p);
});
System.out.println("----------------");
// görüntüleme 2
personnes.forEach(System.out::println);
}
}
- 11. satır: Kodu optimize etmek için, Stream'i yalnızca bir kez oluşturmaya karar verilir. Elde edilen sonuçlar şöyledir:
{"nom":"jean","age":20,"poids":70.0,"sexe":"HOMME"}
{"nom":"marie","age":10,"poids":30.0,"sexe":"FEMME"}
{"nom":"camille","age":30,"poids":55.0,"sexe":"FEMME"}
----------------
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(Unknown Source)
at java.util.stream.ReferencePipeline$Head.forEach(Unknown Source)
at dvp.java8.streams.Exemple02b.main(Exemple02b.java:18)
Bir Stream'i her kullanmak istediğimizde, daha önce oluşturulmuş olsa bile yeniden oluşturmamız gerekir.
5.2. Örnek-02 - Bir akıştaki öğelerin paralel işlenmesi
![]() |
Aşağıdaki kodu ele alalım:
package dvp.java8.streams;
import java.util.List;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple02 {
public static void main(String[] args) {
// kişi listesi
List<Personne> personnes = Personnes.get();
// görüntüleme 1
personnes.stream().forEach(Exemple02::affiche);
System.out.println("-----------------");
// görüntüleme 2
personnes.stream().parallel().forEach(Exemple02::affiche);
}
public static void affiche(Personne p) {
System.out.printf("Personne %s sur thread %s%n", p, Thread.currentThread().getName());
}
}
- 19-21. satırlar: [affiche] yöntemi, bir kişinin jSON dizesini ve görüntülemenin yapıldığı yürütme iş parçacığının adını konsola yazar;
- 13. satır: bir kişi listesi görüntüler. [forEach] yönteminin parametresinin, önceki statik yöntemin referansı olduğu unutulmamalıdır;
- 16. satır: Aynı işlem yapılır, ancak [parallel] yöntemi kullanılarak akıştaki öğelerin işlenmesinin birden fazla iş parçacığı üzerinde paralel olarak gerçekleştirilmesi istenir. Her işlem paralel olarak yapılamaz. Burada, görüntüleme sırasının önemsiz olduğu varsayılmalıdır; çünkü paralel işlemde iş parçacıklarının yürütme sırası garanti edilemez. Ayrıca, hem Stream hem de Observable için yaygın olarak kullanılacak bir sözdizimi dikkat çekmektedir:
- (devam)
- flux, m1 yöntemine veri sağlayan e1 öğeleri üretir;
- flux.m1 ise m2 yöntemine veri sağlayan e2 öğelerinden oluşan bir akıştır;
- flux.m1.m2, m3 yöntemine veri sağlayan e3 öğelerinden oluşan bir akıştır;
e1, e2 ve e3 öğelerinin türü, başlangıç akışının geçirdiği işlemler sırasında değişebilir.
Bu kodun çalıştırılması sonucunda şu çıktı elde edilir:
Görüldüğü gibi, paralel yürütme (5-7. satırlar) üç farklı iş parçacığı üzerinde gerçekleştirilmiştir ve 1-3. satırlardaki öğe sırasını korumamıştır. Bu belgede, bir Stream öğesinin paralel işlenmesine fazla değinmeyeceğiz, çünkü bu durumda bu işlemi mümkün kılan koşulları ele almamız gerekir. Böylece, paralel olarak gerçekleştirilebilecek işlemlerin sayısının az olduğunu görüyoruz. Paralelliğe doğal olarak uygun işlemlerden biri, şimdi sunacağımız bir akıştaki sayısal öğelerin toplamıdır.
5.3. Örnek-03 - Bir Akışın Elemanlarının Paralel İşlenmesi
![]() |
Aşağıdaki kodu ele alalım (Örnek 03a):
package dvp.java8.streams;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Exemple03a {
public static void main(String[] args) {
final long limite = 10_000_000L;
// işlemci sayısı
System.out.printf("La JVM a détecté [%s] coeurs sur votre machine%n", Runtime.getRuntime().availableProcessors());
// sayı listesi
long début = new Date().getTime();
List<Long> nombres = new ArrayList<>();
for (long i = 0; i < limite; i++) {
nombres.add(i);
}
System.out.printf("création de la liste des %s nombres en %s ms%n", limite, new Date().getTime() - début);
// sayıların toplamı - sıralı yöntem
début = new Date().getTime();
long somme = nombres.stream().reduce(0L, (s, i) -> s + i);
System.out.printf("somme séquentielle : somme=%s, durée (ms)=%s%n", somme, new Date().getTime() - début);
}
}
- 22. satırda, imzası aşağıdaki gibi olan [reduce] yöntemini kullanıyoruz:
![]() | ![]() |
- [reduce] yöntemi, T türündeki öğelerle çalışır;
- [reduce] yöntemi, bir akıştaki tüm öğelere aynı işlemi uygular: bir biriktiricinin başlangıç değeri ilk parametre olarak verilir. [BinaryOperator] [2] işlevsel arayüzünü somutlaştıran bir yöntem ikinci parametre olarak verilir: bu yöntem, her bir öğe ve biriktiriciyi kullanarak biriktiricinin yeni bir değerini sağlar. Biriktiricinin nihai değeri, [reduce] yöntemi tarafından döndürülen değerdir. [3] kodu bu mekanizmayı açıklar. [apply] yöntemi, [BinaryOperator] ve [2] işlevsel arayüzünün yöntemidir;
Örnek koda geri dönelim:
- 12. satır: JVM tarafından algılanan çekirdek sayısı görüntülenir;
- 15-18. satırlar: 10 milyon sayıdan oluşan bir liste oluşturulur;
- 22. satır: Bu sayıların toplamı tek bir iş parçacığıyla sırayla hesaplanır;
Şu sonuçlar elde edilir:
Şimdi, kodun 22. satırını aşağıdakiyle değiştirelim (Örnek03b):
long somme = nombres.stream().parallel().reduce(0L, (s, i) -> s + i);
Stream'deki öğelerin birden fazla iş parçacığı kullanılarak paralel olarak işlenmesi istenir. Sayıların toplama sırasının önemi olmadığı için bu mümkündür. Böylece, bir iş parçacığına n1 sayı, başka bir iş parçacığına n2 sayı atayabiliriz (T1, T2, ...) ve sonunda bu farklı iş parçacıklarının sağladığı toplamları toplayabiliriz. Böylece şu sonuçları elde ederiz:
Dolayısıyla performans kazancı neredeyse hiç yoktur. Aşağıdaki örneklerde de genellikle durum böyle olacaktır. İş parçacığı yönetimi de zaman açısından maliyetlidir. Performans kazancının ortaya çıkması için her bir çekirdek tarafından gerçekleştirilen işlemin yeterince karmaşık olması gerekir. Aşağıdaki örnek (Örnek03c) bunu göstermektedir:
package dvp.java8.streams;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.function.BinaryOperator;
public class Exemple03c {
public static void main(String[] args) {
final long limite = 10_000L;
// işlemci sayısı
System.out.printf("La JVM a détecté [%s] coeurs sur votre machine%n", Runtime.getRuntime().availableProcessors());
// sayı listesi
long début = new Date().getTime();
List<Long> nombres = new ArrayList<>();
for (long i = 0; i < limite; i++) {
nombres.add(i);
}
System.out.printf("création de la liste des %s nombres en %s ms%n", limite, new Date().getTime() - début);
// sayıların toplamı - sıralı yöntem
début = new Date().getTime();
BinaryOperator<Long> bo = (s, i) -> {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
return s + i;
};
long somme = nombres.stream().reduce(0L, bo);
System.out.printf("somme séquentielle : somme=%s, durée (ms)=%s%n", somme, new Date().getTime() - début);
}
}
- 30. satır: Yine [reduce] yöntemini kullanıyoruz ve bu yönteme, 23-29. satırlardaki yöntemin referansını parametre olarak veriyoruz;
- 28. satır: [bo] yöntemi, iki parametresinin toplamını verir;
- 24-27. satırlar: Yoğun bir iş yükünü simüle etmek için, iş parçacığını yapay olarak 1 milisaniye bekletilir;
Böylece şu sonuçlar elde edilir:
Şimdi, 30. satırı aşağıdakiyle değiştirirsek:
long somme = nombres.stream().parallel().reduce(0L, bo);
şu sonuçlar elde edilir:
Toplam hesaplamasının paralel olarak yürütülmesinin sağladığı performans artışı açıkça görülüyor. 8 sayının işlenmesi için:
- sıralı iş parçacığı 8 kez 1 milisaniye, yani toplam 8 ms bekler;
- 8 paralel iş parçacığı ise aynı anda her biri 1 milisaniye bekler (basitleştirmek için zihinsel bir örnek), yani 8 sayı için toplamda 1 milisaniye;
Dolayısıyla, paralel yürütmenin sıralı yürütmeden 8 kat daha hızlı olmasını bekleyebiliriz. Burada da durum yaklaşık olarak böyledir.
5.4. Örnek-04 - Bir Stream'i filtreleme
![]() |
Aşağıdaki kodu ele alalım:
package dvp.java8.streams;
import java.util.List;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple04 {
public static void main(String[] args) {
// kişi listesi
List<Personne> personnes = Personnes.get();
// görüntülemeler
System.out.println("age < 28 ----------------------");
personnes.stream().filter(p -> p.getAge() < 28).forEach(p -> {
System.out.println(p);
});
System.out.println("poids < 50 ----------------------");
personnes.stream().filter(p -> p.getPoids() < 50).forEach(p -> {
System.out.println(p);
});
System.out.println("age < 28 ----------------------");
personnes.stream().filter(p -> p.getAge() < 28).forEach(System.out::println);
System.out.println("poids < 50 ----------------------");
personnes.stream().filter(p -> p.getPoids() < 50).forEach(System.out::println);
}
}
- 14. satır: [Stream.filter] yönteminin imzası şöyledir:
![]() |
- [filter] yöntemi, 4.2. paragrafta sunulan ve uygulanması gereken tek yöntemi aşağıdaki gibi olan [Predicate] işlevsel arayüzünün bir örneğini parametre olarak bekler: boolean test(T t);
- [filter] yöntemi, Predicate'i karşılayan Stream öğelerini döndürür. Dolayısıyla, Stream'i filtrelemek için kullanılır;
Aşağıdaki kodu ele alalım:
package dvp.java8.streams;
import java.util.List;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple04 {
public static void main(String[] args) {
// kişi listesi
List<Personne> personnes = Personnes.get();
// görüntülemeler
System.out.println("age < 28 ----------------------");
personnes.stream().filter(p -> p.getAge() < 28).forEach(p -> {
System.out.println(p);
});
System.out.println("poids < 50 ----------------------");
personnes.stream().filter(p -> p.getPoids() < 50).forEach(p -> {
System.out.println(p);
});
System.out.println("age < 28 ----------------------");
personnes.stream().filter(p -> p.getAge() < 28).forEach(System.out::println);
System.out.println("poids < 50 ----------------------");
personnes.stream().filter(p -> p.getPoids() < 50).forEach(System.out::println);
}
}
- 14-16. satırlar: yaşı <28 olan kişileri görüntüler;
- 18-20. satırlar: ağırlığı <50 olan kişileri gösterir;
- 22. satır: 14-16. satırlarla aynı işlevi görür, ancak daha özlü bir şekilde;
- 24. satır: 18-20. satırlarla aynı işlevi görür, ancak daha özlü bir şekilde;
Çalıştırma sonuçları şöyledir:
5.5. Örnek-05 - bir Stream<T1>'den bir Stream<T2> oluşturma
![]() |
Aşağıdaki kodu ele alalım:
package dvp.java8.streams;
import java.util.List;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple05 {
public static void main(String[] args) {
// kişi listesi
List<Personne> personnes = Personnes.get();
// görüntülemeler
System.out.println("Personne --> String ----------------------");
personnes.stream().map(p -> p.getNom()).forEach(System.out::println);
System.out.println("Personne --> Integer ----------------------");
personnes.stream().map(p -> p.getAge()).forEach(System.out::println);
}
}
- 13. satırda, [Stream.map] yönteminin imzası şöyledir:
![]() |
[Stream.map] yönteminin parametresi, 4.3. paragrafta tanıtılan ve uygulanması gereken tek yöntemi R apply(T t) olan [Function] işlevsel arayüzünün bir örneğidir. Görüldüğü gibi, T türünden yola çıkarak [apply] işlevi bir R türü üretir. Dolayısıyla, [Stream.map] yöntemi, T türündeki bir akıştan (burada T türündeki akış, dilin yanlış kullanımına rağmen, T türündeki öğelerden oluşan akış anlamına gelir) R türünde bir Stream akışı üretecektir.
Şimdi örnek kodunu inceleyelim:
- 14. satır: p adlı bir kişiden sadece adı tutulur. Böylece bir String akışı elde edilir;
- 14. satır: p kişisinden sadece adı tutulur. Böylece Integer akışı elde edilir;
Elde edilen sonuçlar şunlardır:
5.6. Örnek-06 - Stream<T> sınıfının diğer yöntemleri
![]() |
Stream sınıfındaki 39 yöntemden bazılarını aşağıdaki kodla gösteriyoruz:
package dvp.java8.streams;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dvp.data.Personne;
import dvp.data.Personnes;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Exemple06 {
// eşleştirici jSON
static private ObjectMapper jsonMapper = new ObjectMapper();
public static void main(String[] args) throws JsonProcessingException {
// kişi listesi
List<Personne> personnes = Personnes.get();
// tüm kişiler
affiche("all", personnes);
// 1. kişi
affiche("findFirst", personnes.stream().findFirst().get());
// herhangi bir kişi
affiche("findAny", personnes.stream().findAny().get());
// 1. kişi hariç kişiler
affiche("skip 1", personnes.stream().skip(1L).collect(Collectors.toList()));
// ilk 2 kişi
affiche("limit 2", personnes.stream().limit(2L).collect(Collectors.toList()));
// kişi sayısı
affiche("count", personnes.stream().count());
// en yaşlı kişi
affiche("age max", personnes.stream().max(Comparator.comparingInt(Personne::getAge)).get());
// en hafif kişi
affiche("poids min", personnes.stream().min(Comparator.comparingDouble(Personne::getPoids)).get());
// isimlerin alfabetik sırasına göre son kişi
affiche("nom max", personnes.stream().max((p1, p2) -> p1.getNom().compareToIgnoreCase(p2.getNom())).get());
// tüm kişilerin toplam yaşı
affiche("âge total (reduce)", personnes.stream().map(p -> p.getAge()).reduce(0, (a1, a2) -> a1 + a2));
// yaşları artan sırayla kişiler
affiche("personnes par âge croissant",
personnes.stream().sorted(Comparator.comparingInt(Personne::getAge)).collect(Collectors.toList()));
// 100 yaşından büyük kişiler var mı?
affiche("des personnes de + de 100 ans (anyMatch)", personnes.stream().anyMatch(p -> p.getAge() > 100));
// tüm kişilerin yaşı en fazla 100 mi?
affiche("des personnes de + de 100 ans (noneMatch)", personnes.stream().noneMatch(p -> p.getAge() > 100));
// Tüm kişilerin yaşı 8'den büyük mü?
affiche("des personnes de + de 8 ans (allMatch)", personnes.stream().allMatch(p -> p.getAge() > 8));
// insanlar cinsiyete göre gruplandırılır mı?
affiche("personnes regroupées par sexe", personnes.stream().collect(Collectors.groupingBy(p -> p.getSexe())));
// Bir listeden yinelenen öğeleri silme
affiche("distinct", Stream.of(1, 2, 1).distinct().collect(Collectors.toList()));
// bir Stream<Stream<T>>'den bir Stream<T> oluşturulur
affiche("flatMap", Stream.of(1, 2, 3).flatMap(i -> Stream.of(i, i + 10)).collect(Collectors.toList()));
// bir Stream<Stream<Integer>>'den bir IntStream oluşturulur ve bunun toplamı hesaplanır
affiche("flatMapToInt", Stream.of(1, 2, 3).flatMapToInt(i -> IntStream.of(i, i + 10)).sum());
// bir Stream<Stream<Integer>>'den, bir DoubleStream oluşturulur, ardından bir dizi
affiche("flatMapToDouble", Stream.of(1, 2, 3).flatMapToDouble(i -> DoubleStream.of(i, i * 1.2)).toArray());
// bir tamsayı akışından max
affiche("reduce Integer::max", Stream.of(1, 10, 8).reduce(Integer::max).get());
// bir Double akışından min
affiche("reduce Integer::min", Stream.of(1.5, 10.4, 8.9).reduce(Double::min).get());
// bir tamsayı akışının ortalaması
affiche("IntStream average", IntStream.of(1, 10, 8).average().getAsDouble());
// bir tamsayı akışının istatistikleri
affiche("IntStream summaryStatistics", IntStream.of(1, 10, 8).summaryStatistics());
}
public static <T> void affiche(String message, T value) throws JsonProcessingException {
System.out.println(String.format("%s ----", message));
System.out.println(jsonMapper.writeValueAsString(value));
}
}
- 72. ve 75. satırlar: yöntemin ikinci parametresindeki jSON dizesini görüntüler;
- 24. satır: tüm kişilerin jSON dizesini görüntüler. Aşağıdaki sonuç elde edilir:
5.6.1. [findFirst]
// 1. kişi
affiche("findFirst", personnes.stream().findFirst().get());
[findFirst] yöntemi, varsa bir akışın ilk öğesini döndürür. İmzası şöyledir:
![]() |
Sonuç, Java 8 ile tanıtılan Optional<T> türündedir:
![]() |
Optional<T> sınıfı, null işaretçilerini farklı şekilde yönetmeye olanak tanır. null değerine sahip olabilen bir T türünü döndürmesi gereken bir yöntem, Optional<T> türünü döndürmeyi tercih edebilir. [Optional<T>.isPresent()] yöntemi, yöntemin bir değer döndürüp döndürmediğini öğrenmeyi sağlar. Aşağıdaki [Exemple06b] kodu, Optional<T>'nin işleyişinin bir kısmını göstermektedir:
package dvp.java8.streams;
import java.util.Optional;
import com.fasterxml.jackson.core.JsonProcessingException;
public class Exemple06b {
public static void main(String[] args) throws JsonProcessingException {
// değeri olmayan isteğe bağlı
Optional<Integer> o1 = m1();
System.out.println(o1.isPresent());
affiche(o1);
// değer içeren isteğe bağlı
Optional<Integer> o2 = m2();
System.out.println(o2.isPresent());
affiche(o2);
}
private static void affiche(Optional<Integer> o1) {
try {
// Optional'ın değeri alınır
// değer yoksa 1 istisna atar
System.out.println(o1.get());
} catch (Throwable th) {
System.out.printf("%s : %s%n", th.getClass().getName(), th.getMessage());
}
}
public static Optional<Integer> m1() {
// değer yok
return Optional.empty();
}
public static Optional<Integer> m2() {
// bir değer
return Optional.of(10);
}
}
Elde edilen sonuçlar şunlardır:
false
java.util.NoSuchElementException : No value present
true
10
[findFirst] yöntemini gösteren koda geri dönelim:
// 1. kişi
affiche("findFirst", personnes.stream().findFirst().get());
- 2. satır: Kodu basitleştirmek için, [findFirst] yöntemi tarafından üretilen Optional<Personne> üzerinde [get] yöntemini kullanıyoruz. Daha temiz bir kod için, [get] yöntemini çağırmadan önce [Optional<Personne>.isPresent()] yöntemini çağırmamız gerekir;
Elde edilen sonuç şöyledir:
5.6.2. [findAny]
// herhangi bir kişi
affiche("findAny", personnes.stream().findAny().get());
[findAny] yönteminin imzası şöyledir:
![]() |
[findAny] yöntemi, akıştaki herhangi bir öğeyi döndürebilir. Testler sırasında, sıralı yürütmede akışın ilk öğesinin döndürüldüğü, paralel yürütmede ise herhangi bir öğenin döndürülebildiği gözlemlenmiştir. Bu durum, aşağıdaki [Exemple06c] koduyla gösterilmektedir:
package dvp.java8.streams;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dvp.data.Personne;
import dvp.data.Personnes;
public class Exemple06c {
// jSON eşleştiricisi
static private ObjectMapper jsonMapper = new ObjectMapper();
public static void main(String[] args) throws JsonProcessingException {
// kişi listesi
List<Personne> personnes = Personnes.get();
// tüm kişiler
affiche("all", personnes);
// herhangi bir kişi
affiche("findAny parallèle", personnes.stream().parallel().findAny().get());
// herhangi bir kişi
affiche("findAny séquentiel", personnes.stream().findAny().get());
}
public static <T> void affiche(String message, T value) throws JsonProcessingException {
System.out.println(String.format("%s ----", message));
System.out.println(jsonMapper.writeValueAsString(value));
}
}
- 22. satır: Paralel olarak yürütülen findAny;
- 24. satır: findAny sıralı olarak yürütülür;
Elde edilen sonuçlar şunlardır:
- 4. satır: Paralel yürütme, kişi listesinin 2. öğesini döndürdü. Bu, başka bir öğe de olabilirdi;
- 6. satır: Sıralı yürütme, kişi listesinin ilk öğesini döndürdü;
[findAny] yönteminin kullanımı, ancak bir akışın paralel işlenmesinde anlamlı görünmektedir.
5.6.3. [skip]
// 1. kişi hariç kişiler
affiche("skip 1", personnes.stream().skip(1L).collect(Collectors.toList()));
[skip] yönteminin imzası şöyledir:
![]() |
[skip] yöntemi, bir akışın ilk n öğesini atlar. Yukarıdaki belgelerde belirtildiği gibi, bu yöntemin paralel olarak çalıştırılması performans açısından çok az kazanç sağlar ve hatta performans kaybına neden olabilir. Nitekim, ilk n öğeyi atlamak için iş parçacıkları birbirleriyle koordinasyon sağlamak zorunda kalır ve bu da paralellikten kaynaklanan performans kazancını ortadan kaldırır.
[skip] yöntemi, bir Stream<Personne> akışını döndürür; bu akış, aşağıdaki imzaya sahip [collect] yöntemi tarafından List<Personne> türüne dönüştürülür:
![]() |
[collect] yöntemi, imzası karmaşık olan [Collector] türünden bir örneği parametre olarak kabul eder. [Collector] türünün önceden tanımlanmış uygulamaları mevcuttur ve bunlar çoğu zaman bu türü kendiniz uygulamak zorunda kalmamanızı sağlar. Burada kullanılan uygulama [Collectors.toList()]'tir. [Collectors], [Collector<T,A,R>] türünü uygulayan çok sayıda statik yönteme sahip bir sınıftır. Bir Stream'i standart bir Java koleksiyonuna dönüştürmek istediğinizde, öncelikle burada aramalısınız:
![]() |
Daha sonra bu yöntemlerden bazılarını kullanacağız.
Çalıştırıldığında şu sonuç elde edilir:
Listenin ilk öğesi (jean) atlanmıştır.
5.6.4. [limit]
// ilk 2 kişi hariç
affiche("limit 2", personnes.stream().limit(2L).collect(Collectors.toList()));
[limit] yönteminin imzası şöyledir:
![]() |
[limit] yöntemi, bir akışın yalnızca ilk n öğesini tutmaya olanak tanır. Bu yöntem, paralel işleme uygun değildir.
Yürütme sonucunda şu çıktı elde edilir:
5.6.5. [count]
// kişi sayısı
affiche("count", personnes.stream().count());
[count] yönteminin imzası şöyledir:
![]() |
[count] yöntemi, bir Stream'in eleman sayısını döndürür. Aşağıdaki kodda (Örnek06d1) gösterildiği gibi, yöntemin paralel olarak çalıştırılması performans artışı sağlamaz:
package dvp.java8.streams;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Stream;
public class Exemple06d1 {
public static void main(String[] args) {
final long limite = 10_000_000L;
// işlemci sayısı
System.out.printf("La JVM a détecté [%s] coeurs sur votre machine%n", Runtime.getRuntime().availableProcessors());
// sayı listesi
long début = new Date().getTime();
List<Long> nombres = new ArrayList<>();
for (long i = 0; i < limite; i++) {
nombres.add(i);
}
System.out.printf("création de la liste des %s nombres en %s ms%n", limite, new Date().getTime() - début);
// sayıların sayılması - sıralı yöntem
Stream<Long> sNombres = nombres.stream();
début = new Date().getTime();
long count = sNombres.count();
System.out.printf("comptage séquentiel : compteur=%s, durée (ms)=%s%n", count, new Date().getTime() - début);
}
}
- 11-22. satırlar: 10 milyon sayıdan oluşan bir Stream oluşturulur;
- 22-24. satırlar: Stream'in sayımı;
Çalıştırıldığında şu sonuç elde edilir:
Kodun 22. satırını aşağıdakiyle değiştirirsek (Örnek06d2):
Stream<Long> sNombres = nombres.stream().parallel();
aşağıdaki sonuçlar elde edilir:
5.6.6. [max, min]
// en yaşlı kişi
affiche("age max", personnes.stream().max(Comparator.comparingInt(Personne::getAge)).get());
[max] yönteminin imzası şöyledir:
![]() |
[max] yöntemi, kendisine parametre olarak geçirilen karşılaştırıcıyı kullanarak bir akışın maksimum değerini döndürür. Comparator, uygulanması gereken tek yöntemin imzası int compare (T o1, T o2) olan bir işlevsel arayüzdür. Bu yöntem, o1 < o2 ise -1, o1.equals(o2) ise 0, o1 > o2 ise +1 değerini döndürmelidir. Comparator işlevsel arayüzü, en yaygın durumlar için Comparator arayüzünü uygulayan birçok varsayılan statik yönteme sahiptir. Dolayısıyla şu komutta:
affiche("age max", personnes.stream().max(Comparator.comparingInt(Personne::getAge)).get());
şu imzaya sahip [Comparator.comparingInt] statik yöntemini kullanıyoruz:
![]() |
ToIntFunction türü işlevsel bir arayüzdür:
![]() |
ToIntFunction işlevsel arayüzünün [applyAsInt] yöntemi, bir T türünden int türünü üretir. Kodumuza geri dönelim:
affiche("age max", personnes.stream().max(Comparator.comparingInt(Personne::getAge)).get());
[Comparator.comparingInt] yönteminin etkin parametresi burada bir Personne --> int lambda olmalıdır. Bu imzaya sahip olan [Personne.getAge] yönteminin referansını aktarıyoruz. Sonuç olarak, en büyük yaşa sahip kişiyi elde edeceğiz. Optional<Personne> türünde bir değer elde ediyoruz ve bu değerden [Optional.get] yöntemi ile değeri çıkarıyoruz. Sonuç şu şekilde oluyor:
max'in paralel olarak hesaplanması, aşağıdaki örnekte de görüldüğü gibi performans artışı sağlamaz: (Örnek06e1):
package dvp.java8.streams;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;
public class Exemple06e1 {
public static void main(String[] args) {
// tarih
// final long limit = 100L;
// final boolean verbose = true;
final long limite = 10_000_000L;
final boolean verbose = false;
// işlemci sayısı
System.out.printf("La JVM a détecté [%s] coeurs sur votre machine%n", Runtime.getRuntime().availableProcessors());
// sayı listesi
long début = new Date().getTime();
List<Long> nombres = new ArrayList<>();
for (long i = 0; i < limite; i++) {
nombres.add(new Random().nextLong());
}
System.out.printf("création de la liste des %s nombres en %s ms%n", limite, new Date().getTime() - début);
// sayıların maksimum değeri - sıralı yöntem
Stream<Long> sNombres = nombres.stream();
Comparator<Long> compLong = (l1, l2) -> {
if (verbose) {
// iş parçacığı
System.out.printf("[%s]", Thread.currentThread().getName());
}
// karşılaştırma
long v1 = l1.longValue();
long v2 = l2.longValue();
if (v1 < v2) {
return -1;
} else {
if (v1 == v2) {
return 0;
} else {
return +1;
}
}
};
début = new Date().getTime();
// en uzun = sNombres.max(Comparator.naturalOrder()).get();
long max = sNombres.max(compLong).get();
System.out.printf("%nmax séquentiel : max=%s, durée (ms)=%s%n", max, new Date().getTime() - début);
}
}
- 29. satır: limite türünde rastgele sayı akışı vardır;
- 30-47. satırlar: compLong lambda değişkeni, Comparator<Long> arayüzünü uygular. Bu arayüz normalde 49. satırdaki [Comparator.naturalOrder()] yöntemi ile uygulanır. Ancak burada, yürütme iş parçacığını görüntülemek istiyoruz (31-33. satırlar). Bu nedenle arayüzü kendimiz uyguluyoruz;
- 50. satır: max aranıyor;
Şu sonuçları elde ediyoruz:
![]() |
Şimdi, 27. satırı aşağıdakiyle değiştirirsek (Örnek06e2):
Stream<Long> sNombres = nombres.stream().parallel();
şu sonuçlar elde edilir:
![]() |
Dolayısıyla paralel yürütme daha yavaştı. verbose=false ile 10 milyon sayıya geçersek, şu sonuçları elde ederiz:
Sıralı yürütme için:
paralel yürütme için; bu da yine daha yavaş kalıyor.
[Stream.min] yöntemini de benzer şekilde kullanıyoruz:
// en hafif kişi
affiche("poids min", personnes.stream().min(Comparator.comparingDouble(Personne::getPoids)).get());
5.6.7. [reduce]
// tüm kişilerin toplam yaşı
affiche("âge total (reduce)", personnes.stream().map(p -> p.getAge()).reduce(0, (a1, a2) -> a1 + a2));
[reduce] yöntemi 5.3. paragrafta tanıtılmıştır. Yukarıdaki 2. satır, tüm kişilerin yaşlarını toplar. Sonuç şöyledir:
5.6.8. [sorted]
// yaşları artan sırayla kişiler
affiche("personnes par âge croissant",
personnes.stream().sorted(Comparator.comparingInt(Personne::getAge)).collect(Collectors.toList()));
// isimlerine göre alfabetik sırayla sıralanmış kişiler
List<Personne> lPersonnes=personnes.stream().sorted((p1, p2) -> p1.getNom().compareTo(p2.getNom())).collect(Collectors.toList());
affiche("personnes par ordre alphabétique des noms", lPersonnes);
[sorted] yöntemi (3. ve 5. satırlar) aşağıdaki imzaya sahiptir:
![]() |
[sorted] yöntemi, 5.6.6 paragrafında min ve max yöntemleri için açıklanan [Comparator] türünü parametre olarak kabul eder. Bu yöntem, bir Stream nesnesini, parametre olarak geçirilen karşılaştırıcıya göre sıralamaya olanak tanır. [Comparator] arayüzünün, özellikle sayılar ve karakter dizileri için yaygın olarak kullanılan karşılaştırıcıları uygulayan birkaç varsayılan statik yöntem sunduğunu görmüştük. Burada, parametre olarak ToIntFunction türünü kabul eden [Comparator.comparingInt] yöntemini kullanıyoruz; bu, aşağıdaki imzaya sahip [applyAsInt] yönteminin işlevsel bir arayüzüdür: int applyAsInt(T t). Burada, 3. satırda [Comparator.comparingInt] yöntemine geçirilen etkin parametre, kişinin yaşını veren [Personne.age] yönteminin referansıdır.
[Comparator] arayüzü, karakter dizelerini karşılaştırmak için statik yöntemler sunmamaktadır. 5. satırda, bu arayüzün tek yöntemini uygulayan bir lambda ifadesini kendimiz oluşturuyoruz: int compare(T t1, T t2)
(p1, p2) -> p1.getNom().compareTo(p2.getNom())
Bu lambda, kişilerin isimlerini karşılaştırır. Elde edilen sonuçlar şunlardır:
Aşağıdaki kodda (Örnek06f1) görüldüğü gibi, sıralamanın paralel olarak yürütülmesi mümkün görünmüyor:
package dvp.java8.streams;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Exemple06f1 {
// jSON eşleştiricisi
static ObjectMapper jsonMapper = new ObjectMapper();
public static void main(String[] args) throws JsonProcessingException {
// veriler
final long limite = 100L;
final boolean verbose = true;
// son uzun sınır = 10_000_000L;
// final boolean ayrıntılı = false;
// işlemci sayısı
System.out.printf("La JVM a détecté [%s] coeurs sur votre machine%n", Runtime.getRuntime().availableProcessors());
// sayı listesi
long début = new Date().getTime();
List<Integer> nombres = new ArrayList<>();
for (long i = 0; i < limite; i++) {
nombres.add(new Random().nextInt(1000));
}
System.out.printf("création de la liste des %s nombres en %s ms%n", limite, new Date().getTime() - début);
// sayıların sıralanması - sıralı yöntem
Stream<Integer> sNombres = nombres.stream();
début = new Date().getTime();
Comparator<Integer> compInt = (i1, i2) -> {
if (verbose) {
// iş parçacığı
System.out.printf("[%s]", Thread.currentThread().getName());
}
// karşılaştırma
int v1 = i1.intValue();
int v2 = i2.intValue();
if (v1 < v2) {
return +1;
} else {
if (v1 == v2) {
return 0;
} else {
return -1;
}
}
};
if (verbose) {
affiche("nombres", sNombres.sorted(compInt).collect(Collectors.toList()));
}
System.out.printf("tri séquentiel : durée (ms)=%s%n", new Date().getTime() - début);
}
public static <T> void affiche(String message, T value) throws JsonProcessingException {
System.out.println(String.format("%s ----", message));
System.out.println(jsonMapper.writeValueAsString(value));
}
}
- 30-36. satırlar: limite rastgele sayı akışı oluşturulur;
- 32. satırda, compInt lambda (38-55. satırlar) [sorted] yöntemine aktarılır. Bu lambda, sayıları azalan sırada sıralar ve onu çalıştıran iş parçacığını görüntüler.
Elde edilen sonuçlar şunlardır:
![]() |
Yukarıdaki kodda 36. satırı aşağıdakiyle değiştirirsek (Örnek06f2):
Stream<Integer> sNombres = nombres.stream().parallel();
şu sonuçlar elde edilir:
![]() |
Şaşırtıcı bir şekilde, sayı akışının sıralamasının tek bir iş parçacığıyla yapıldığını görüyoruz. Hiçbir paralel işlem gerçekleşmemiştir. Yoksa gözümden kaçan bir şey mi var?
5.6.9. [anyMatch, noneMatch, allMatch]
// 100 yaşından büyük kişiler var mı?
affiche("des personnes de + de 100 ans (anyMatch)", personnes.stream().anyMatch(p -> p.getAge() > 100));
// tüm insanların yaşı en fazla 100 mi?
affiche("des personnes de + de 100 ans (noneMatch)", personnes.stream().noneMatch(p -> p.getAge() > 100));
// Herkes 8 yaşından büyük mü?
affiche("des personnes de + de 8 ans (allMatch)", personnes.stream().allMatch(p -> p.getAge() > 8));
2., 4. ve 6. satırlarda, [anyMatch, noneMatch, allMatch] yöntemlerinin parametresi, 4.2. paragrafında açıklanan Predicate türüdür. Dolayısıyla bu yöntemler bir filtreleme işlemi gerçekleştirir. Üçü de bir boole değeri döndürür:
- anyMatch, Stream öğeleri arasında filtreyi karşılayan en az bir öğe varsa true değerini döndürür;
- noneMatch, Stream'te filtreyi karşılayan hiçbir öğe yoksa true'i verir;
- allMatch, Stream'teki tüm öğeler filtreyi karşılıyorsa true sonucunu verir;
Elde edilen sonuçlar şunlardır:
5.6.10. [collect(Collectors.groupingBy)]
// insanlar cinsiyete göre gruplandırılıyor mu?
affiche("personnes regroupées par sexe", personnes.stream().collect(Collectors.groupingBy(p -> p.getSexe())));
[collect] yöntemi 5.6.3. paragrafında tanıtılmıştır. Bu yöntemin parametresi, [Collector] arayüzünün bir uygulamasıdır. [Collectors] sınıfı, [Collector] arayüzünü uygulayan bir dizi statik yöntem sunar. Şimdiye kadar [Collectors.toList()] yöntemini kullandık. Burada, Stream'ten bir sözlük oluşturan [Collectors.groupingBy] statik yöntemini kullanıyoruz. İmzası şöyledir:
![]() |
[groupingBy] yöntemi, bir Stream<T> türünden bir Map<K,List<T>> türü oluşturur. K anahtarı, Function<T,K> türündeki [groupingBy] yönteminin parametresi tarafından sağlanır; bu yöntemin tek yönteminin imzası şöyledir: K apply(T t). Kişilerin cinsiyetine göre indekslenmiş bir sözlük oluşturmak istiyorsak, bir kişiden cinsiyetini üreten bir fonksiyon sağlamamız gerekir. Burada, [groupingBy] yönteminin etkin parametresi olarak [Personne.getSexe] yönteminin referansını aktarıyoruz. Elde edilen sonuçlar şunlardır:
- satırda, jSON dizesi, HOMME ve FEMME olmak üzere iki anahtarla indekslenmiş bir sözlükte yer almaktadır.
Aşağıdaki örnekte (Örnek06g1) görüldüğü gibi, paralel hesaplama performans artışı sağlamamaktadır:
package dvp.java8.streams;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Exemple06g1 {
// mppeur jSON
static ObjectMapper jsonMapper = new ObjectMapper();
public static void main(String[] args) throws JsonProcessingException {
// veriler
final long limite = 100L;
final boolean verbose = true;
// son uzun sınır = 10_000_000L;
// final boolean ayrıntılı = false;
// işlemci sayısı
System.out.printf("La JVM a détecté [%s] coeurs sur votre machine%n", Runtime.getRuntime().availableProcessors());
// sayı listesi
long début = new Date().getTime();
List<Integer> nombres = new ArrayList<>();
for (long i = 0; i < limite; i++) {
nombres.add(new Random().nextInt(1000));
}
System.out.printf("création de la liste des %s nombres en %s ms%n", limite, new Date().getTime() - début);
// sayıların yüzler halinde gruplandırılması - sıralı yöntem
Stream<Integer> sNombres = nombres.stream();
Function<Integer, Integer> groupByCent = n -> {
if (verbose) {
System.out.printf("[%s]", Thread.currentThread().getName());
}
return n / 100;
};
début = new Date().getTime();
// Map<Integer, List<Integer>> lNombres = sNombres.collect(Collectors.groupingBy(sayı -> sayı / 100));
Map<Integer, List<Integer>> lNombres = sNombres.collect(Collectors.groupingBy(groupByCent));
System.out.printf("%nregroupement séquentiel : durée (ms)=%s%n", new Date().getTime() - début);
// sonuçlar
if (verbose) {
affiche("nombres regroupés", lNombres);
}
}
public static <T> void affiche(String message, T value) throws JsonProcessingException {
System.out.println(String.format("%s ----", message));
System.out.println(jsonMapper.writeValueAsString(value));
}
}
- 23-38. satırlar: limite sayı akışının oluşturulması;
- 47. satırda, sayılar yüzlük gruplar halinde toplanır. Yürütme iş parçacığını görüntüleyebilmek için 39-44. satırlardaki lambda işlevi kullanılır;
Çalıştırma sonuçları şöyledir:
![]() |
Kodda 38. satırı aşağıdaki satırla değiştirirsek (Örnek06g2):
Stream<Integer> sNombres = nombres.stream().parallel();
şu sonuçlar elde edilir:
![]() |
Görüldüğü gibi, gruplandırmanın paralel olarak yürütülmesi performans düşüşüne neden olmuştur.
5.6.11. [distinct]
// bir listeden yinelenen öğelerin silinmesi
affiche("distinct", Stream.of(1, 2, 1).distinct().collect(Collectors.toList()));
[distinct] yönteminin imzası şöyledir:
![]() |
Bu yöntem, bir akıştaki yinelenen öğeleri ortadan kaldırmaya yarar. [Stream.of] yöntemi (2. satır) aşağıdaki imzaya sahiptir:
![]() |
Bu yöntem, açıkça belirtilen değerlerden bir Stream oluşturmaya yarar. Yürütme sonuçları şöyledir:
5.6.12. [flatMap]
// bir Stream<Stream<T>>'den bir Stream<T> oluşturulur
affiche("flatMap", Stream.of(1, 2, 3).flatMap(i -> Stream.of(i, i + 10)).collect(Collectors.toList()));
[flatMap] yönteminin imzası şöyledir:
![]() ![]() |
[flatMap] yöntemi, parametre olarak aşağıdakileri karşılayan bir işlevi kabul eder:
- Stream'in T türünde bir elemanını parametre olarak alır;
- sonuç olarak bir Stream<R> akışı döndürür;
Eğer [flatMap] yöntemi yerine, 5.5. paragrafta açıklanan [map] yöntemi kullanılsaydı, sonuç bir Stream<Stream<R>> türü olurdu; burada başlangıç akışındaki her T türündeki eleman, bir Stream<R> elemanına dönüşürdü. [flatMap] yöntemi ise Stream<R> türünü verir. Bu yöntem, farklı Stream<R> akışlarını tek bir akışa düzleştirir (flatten). Yukarıdaki kodun çalıştırılmasının sonuçları da bunu göstermektedir:
[flatMap]'in özel varyantları da mevcuttur:
// bir Stream<IntStream>'den bir IntStream oluşturulur ve bunun toplamı hesaplanır
affiche("flatMapToInt", Stream.of(1, 2, 3).flatMapToInt(i -> IntStream.of(i, i + 10)).sum());
[flatMapToInt] yönteminin imzası şöyledir:
![]() |
[flatMapToInt] yöntemi, sonucu aşağıdaki IntStream türünde olan bir işlevi parametre olarak kabul eder:
![]() |
IntStream, int'in bir akışıdır. Bu tür, Stream<Integer> türüne tercih edilir çünkü işlenmesi sırasında Integer ve int türleri arasında boxing/unboxing işlemlerini ortadan kaldırır. Bu arayüz, Stream<T> türünün birçok yöntemini devralır ve bunlara, yukarıdaki [sum] yöntemi gibi, IntStream'in elemanlarını toplayan yöntemler de ekler.
Aşağıdaki kod, benzer [flatMapToDouble] yönteminin kullanımını göstermektedir:
// bir Stream<DoubleStream>'den, bir DoubleStream oluşturulur ve ardından bir dizi oluşturulur
affiche("flatMapToDouble", Stream.of(1, 2, 3).flatMapToDouble(i -> DoubleStream.of(i, i * 1.2)).toArray());
[DoubleStream.toArray] yöntemi, DoubleStream türünden double[] türüne geçiş yapılmasına olanak tanır.
Bu iki örnek için sonuçlar şu şekildedir:
Aşağıdaki örnek, Stream<Long> türünden LongStream türüne geçildiğinde elde edilen performans artışını göstermektedir (Örnek06i1):
package dvp.java8.streams;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Exemple06i1 {
public static void main(String[] args) {
final long limite = 10_000_000L;
// işlemci sayısı
System.out.printf("La JVM a détecté [%s] coeurs sur votre machine%n", Runtime.getRuntime().availableProcessors());
// sayı listesi
long début = new Date().getTime();
List<Long> nombres = new ArrayList<>();
for (long i = 0; i < limite; i++) {
nombres.add(i);
}
System.out.printf("création de la liste des %s nombres en %s ms%n", limite, new Date().getTime() - début);
// sayıların toplamı - sıralı yöntem
début = new Date().getTime();
long somme = nombres.stream().reduce(0L, (s, i) -> s + i);
System.out.printf("somme séquentielle du Stream<Integer> : somme=%s, durée (ms)=%s%n", somme, new Date().getTime() - début);
}
}
- 22. satır: Long türündeki sayı akışının toplamının hesaplanması;
Aşağıdaki sonuçlar elde edilir:
Şimdi, 22. satırı aşağıdakiyle değiştirelim (Örnek06i2):
long somme = nombres.stream().mapToLong(n -> n.longValue()).sum();
Stream<Integer>.mapToLong yöntemi, LongStream türünde bir akış elde etmemizi sağlar; bu akış, long türündeki temel elemanlardan oluşur ve daha sonra sum işleviyle toplanır. Böylece şu sonuçlar elde edilir:
Performans artışı belirgindir.
5.6.13. ilkel sayı akış yöntemleri
// bir int akışının maksimum değeri
affiche("IntStream max", IntStream.of(1, 10, 8).max());
// double akışının minimum değeri
affiche("DoubleStream min", DoubleStream.of(1.5, 10.4, 8.9).min());
// int akışının ortalaması
affiche("IntStream average", IntStream.of(1, 10, 8).average().getAsDouble());
// bir int akışının istatistikleri
affiche("IntStream summaryStatistics", IntStream.of(1, 10, 8).summaryStatistics());
Temel değer akışları (int, long, double), bu türlere uygun yöntemler sunar. Önceki kodun çalıştırılması sonucunda şu sonuç elde edilir:
- Kodun 2. satırının sonucu, Optional<Integer> türüne benzer bir OptionalInt türüdür. Bu nesnede depolanan değer, [getAsInt()] yöntemi ile elde edilebilir. Bir değerin varlığı, [isPresent()] yöntemi ile test edilebilir. Sonuçların 2. satırı, [OptionalInt] sınıfının [asInt, present] adlı alanlara sahip olduğu anlamına gelmez. Varsayılan olarak, jSON kütüphanesi, jSON formatına serileştirilecek nesnenin tüm getX ve isY genel yöntemlerini kullanır. Ve burada, [asInt, present] alanları mevcut olmamasına rağmen bir [getAsInt] yöntemi ve bir başka [isPresent] yöntemi bulunmaktadır;
- kodun 4. satırının sonucu, Optional<Double> türüne benzer bir OptionalDouble türüdür;
- kodun 6. satırının sonucu, değeri [getAsDouble()] yöntemiyle elde edilebilen OptionalDouble türüdür. [average] yöntemi, sayı akışının ortalamasını hesaplar;
- kodun 8. satırının sonucu, aşağıdaki şekilde tanımlanan IntSummaryStatistics türündedir:
![]() |
Elde edilen IntSummaryStatistics nesnesinin, değer sayısı, toplam, maksimum, minimum ve ortalama gibi sayı akışıyla ilgili çeşitli bilgiler sağladığı görülmektedir.




































