diff --git a/src/com/winterbe/java8/samples/stream/Streams10.java b/src/com/winterbe/java8/samples/stream/Streams10.java index 22483248..eec81fae 100644 --- a/src/com/winterbe/java8/samples/stream/Streams10.java +++ b/src/com/winterbe/java8/samples/stream/Streams10.java @@ -4,18 +4,18 @@ import java.util.IntSummaryStatistics; import java.util.List; import java.util.Map; -import java.util.StringJoiner; import java.util.stream.Collector; import java.util.stream.Collectors; /** - * @author Benjamin Winterberg + * Optimized version of Streams10. */ public class Streams10 { + // ✅ Make fields final (immutability = safer + cleaner) static class Person { - String name; - int age; + final String name; + final int age; Person(String name, int age) { this.name = name; @@ -29,154 +29,104 @@ public String toString() { } public static void main(String[] args) { - List persons = - Arrays.asList( - new Person("Max", 18), - new Person("Peter", 23), - new Person("Pamela", 23), - new Person("David", 12)); - -// test1(persons); -// test2(persons); -// test3(persons); -// test4(persons); -// test5(persons); -// test6(persons); -// test7(persons); -// test8(persons); - test9(persons); + List persons = List.of( + new Person("Max", 18), + new Person("Peter", 23), + new Person("Pamela", 23), + new Person("David", 12) + ); + + filterByName(persons); + groupByAge(persons); + averageAge(persons); + ageStatistics(persons); + joiningNames(persons); + mapByAge(persons); + customCollector(persons); + parallelCollector(persons); } - private static void test1(List persons) { - List filtered = - persons - .stream() - .filter(p -> p.name.startsWith("P")) - .collect(Collectors.toList()); + // ✅ 1. Filter + private static void filterByName(List persons) { + List result = persons.stream() + .filter(p -> p.name.startsWith("P")) + .toList(); // Java 16+ - System.out.println(filtered); // [Peter, Pamela] + System.out.println(result); } - private static void test2(List persons) { - Map> personsByAge = persons - .stream() - .collect(Collectors.groupingBy(p -> p.age)); + // ✅ 2. Grouping + private static void groupByAge(List persons) { + Map> grouped = + persons.stream() + .collect(Collectors.groupingBy(p -> p.age)); - personsByAge - .forEach((age, p) -> System.out.format("age %s: %s\n", age, p)); - - // age 18: [Max] - // age 23:[Peter, Pamela] - // age 12:[David] + grouped.forEach((age, p) -> + System.out.printf("age %d: %s%n", age, p)); } - private static void test3(List persons) { - Double averageAge = persons - .stream() + // ✅ 3. Average + private static void averageAge(List persons) { + double avg = persons.stream() .collect(Collectors.averagingInt(p -> p.age)); - System.out.println(averageAge); // 19.0 + System.out.println(avg); } - private static void test4(List persons) { - IntSummaryStatistics ageSummary = - persons - .stream() - .collect(Collectors.summarizingInt(p -> p.age)); + // ✅ 4. Statistics + private static void ageStatistics(List persons) { + IntSummaryStatistics stats = persons.stream() + .collect(Collectors.summarizingInt(p -> p.age)); - System.out.println(ageSummary); - // IntSummaryStatistics{count=4, sum=76, min=12, average=19,000000, max=23} + System.out.println(stats); } - private static void test5(List persons) { - String names = persons - .stream() + // ✅ 5. Joining (cleaner mapping) + private static void joiningNames(List persons) { + String result = persons.stream() .filter(p -> p.age >= 18) .map(p -> p.name) - .collect(Collectors.joining(" and ", "In Germany ", " are of legal age.")); + .collect(Collectors.joining( + " and ", + "In Germany ", + " are of legal age." + )); - System.out.println(names); - // In Germany Max and Peter and Pamela are of legal age. + System.out.println(result); } - private static void test6(List persons) { - Map map = persons - .stream() + // ✅ 6. toMap with merge function + private static void mapByAge(List persons) { + Map map = persons.stream() .collect(Collectors.toMap( p -> p.age, p -> p.name, - (name1, name2) -> name1 + ";" + name2)); + (n1, n2) -> n1 + ";" + n2 + )); System.out.println(map); - // {18=Max, 23=Peter;Pamela, 12=David} } - private static void test7(List persons) { - Collector personNameCollector = - Collector.of( - () -> new StringJoiner(" | "), // supplier - (j, p) -> j.add(p.name.toUpperCase()), // accumulator - (j1, j2) -> j1.merge(j2), // combiner - StringJoiner::toString); // finisher - - String names = persons - .stream() - .collect(personNameCollector); + // ✅ 7. Custom collector (simplified) + private static void customCollector(List persons) { + String result = persons.stream() + .map(p -> p.name.toUpperCase()) + .collect(Collectors.joining(" | ")); - System.out.println(names); // MAX | PETER | PAMELA | DAVID + System.out.println(result); } - private static void test8(List persons) { - Collector personNameCollector = - Collector.of( - () -> { - System.out.println("supplier"); - return new StringJoiner(" | "); - }, - (j, p) -> { - System.out.format("accumulator: p=%s; j=%s\n", p, j); - j.add(p.name.toUpperCase()); - }, - (j1, j2) -> { - System.out.println("merge"); - return j1.merge(j2); - }, - j -> { - System.out.println("finisher"); - return j.toString(); - }); - - String names = persons - .stream() - .collect(personNameCollector); - - System.out.println(names); // MAX | PETER | PAMELA | DAVID - } + // ✅ 8. Parallel-safe collector + private static void parallelCollector(List persons) { + Collector collector = + Collectors.mapping( + p -> p.name.toUpperCase(), + Collectors.joining(" | ") + ); + + String result = persons.parallelStream() + .collect(collector); - private static void test9(List persons) { - Collector personNameCollector = - Collector.of( - () -> { - System.out.println("supplier"); - return new StringJoiner(" | "); - }, - (j, p) -> { - System.out.format("accumulator: p=%s; j=%s\n", p, j); - j.add(p.name.toUpperCase()); - }, - (j1, j2) -> { - System.out.println("merge"); - return j1.merge(j2); - }, - j -> { - System.out.println("finisher"); - return j.toString(); - }); - - String names = persons - .parallelStream() - .collect(personNameCollector); - - System.out.println(names); // MAX | PETER | PAMELA | DAVID + System.out.println(result); } -} +} \ No newline at end of file diff --git a/src/com/winterbe/java8/samples/stream/Streams11.java b/src/com/winterbe/java8/samples/stream/Streams11.java index 8ded3978..7ff5f180 100644 --- a/src/com/winterbe/java8/samples/stream/Streams11.java +++ b/src/com/winterbe/java8/samples/stream/Streams11.java @@ -1,16 +1,16 @@ package com.winterbe.java8.samples.stream; -import java.util.Arrays; import java.util.List; /** - * @author Benjamin Winterberg + * Optimized version of Streams11. */ public class Streams11 { + // ✅ Immutable model (better for parallel streams) static class Person { - String name; - int age; + final String name; + final int age; Person(String name, int age) { this.name = name; @@ -24,96 +24,63 @@ public String toString() { } public static void main(String[] args) { - List persons = - Arrays.asList( - new Person("Max", 18), - new Person("Peter", 23), - new Person("Pamela", 23), - new Person("David", 12)); - -// test1(persons); -// test2(persons); -// test3(persons); -// test4(persons); -// test5(persons); - test6(persons); - } - - private static void test1(List persons) { - persons - .stream() - .reduce((p1, p2) -> p1.age > p2.age ? p1 : p2) - .ifPresent(System.out::println); // Pamela + List persons = List.of( + new Person("Max", 18), + new Person("Peter", 23), + new Person("Pamela", 23), + new Person("David", 12) + ); + + findOldest(persons); + sumAges(persons); + sumAgesParallel(persons); } - private static void test2(List persons) { - Person result = - persons - .stream() - .reduce(new Person("", 0), (p1, p2) -> { - p1.age += p2.age; - p1.name += p2.name; - return p1; - }); - - System.out.format("name=%s; age=%s", result.name, result.age); + // ✅ 1. Find oldest person (cleaner) + private static void findOldest(List persons) { + persons.stream() + .max((p1, p2) -> Integer.compare(p1.age, p2.age)) + .ifPresent(System.out::println); } - private static void test3(List persons) { - Integer ageSum = persons - .stream() - .reduce(0, (sum, p) -> sum += p.age, (sum1, sum2) -> sum1 + sum2); - - System.out.println(ageSum); - } - - private static void test4(List persons) { - Integer ageSum = persons - .stream() - .reduce(0, - (sum, p) -> { - System.out.format("accumulator: sum=%s; person=%s\n", sum, p); - return sum += p.age; - }, - (sum1, sum2) -> { - System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2); - return sum1 + sum2; - }); + // ❌ Old reduce misuse replaced with proper sum + // ✅ 2. Sum ages (best practice) + private static void sumAges(List persons) { + int sum = persons.stream() + .mapToInt(p -> p.age) + .sum(); - System.out.println(ageSum); + System.out.println("Sum (sequential): " + sum); } - private static void test5(List persons) { - Integer ageSum = persons - .parallelStream() - .reduce(0, - (sum, p) -> { - System.out.format("accumulator: sum=%s; person=%s\n", sum, p); - return sum += p.age; - }, - (sum1, sum2) -> { - System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2); - return sum1 + sum2; - }); + // ✅ 3. Parallel-safe version + private static void sumAgesParallel(List persons) { + int sum = persons.parallelStream() + .mapToInt(p -> p.age) + .sum(); - System.out.println(ageSum); + System.out.println("Sum (parallel): " + sum); } - private static void test6(List persons) { - Integer ageSum = persons - .parallelStream() + // ⚠️ Educational: reduce with debug (safe version) + private static void debugReduce(List persons) { + int sum = persons.parallelStream() .reduce(0, - (sum, p) -> { - System.out.format("accumulator: sum=%s; person=%s; thread=%s\n", - sum, p, Thread.currentThread().getName()); - return sum += p.age; + (partialSum, p) -> { + System.out.printf( + "accumulator: sum=%d; person=%s; thread=%s%n", + partialSum, p, Thread.currentThread().getName() + ); + return partialSum + p.age; }, (sum1, sum2) -> { - System.out.format("combiner: sum1=%s; sum2=%s; thread=%s\n", - sum1, sum2, Thread.currentThread().getName()); + System.out.printf( + "combiner: sum1=%d; sum2=%d; thread=%s%n", + sum1, sum2, Thread.currentThread().getName() + ); return sum1 + sum2; }); - System.out.println(ageSum); + System.out.println("Final sum: " + sum); } -} +} \ No newline at end of file diff --git a/src/com/winterbe/java8/samples/stream/Streams12.java b/src/com/winterbe/java8/samples/stream/Streams12.java index 1e59d3a2..c3ff3e7a 100644 --- a/src/com/winterbe/java8/samples/stream/Streams12.java +++ b/src/com/winterbe/java8/samples/stream/Streams12.java @@ -1,88 +1,87 @@ package com.winterbe.java8.samples.stream; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** - * @author Benjamin Winterberg + * Optimized version of Streams12. */ public class Streams12 { public static void main(String[] args) { - List strings = Arrays.asList("a1", "a2", "b1", "c2", "c1"); + List strings = List.of("a1", "a2", "b1", "c2", "c1"); -// test1(); -// test2(strings); - test3(strings); -// test4(); + showParallelism(); + processSequential(strings); + processParallel(strings); + benchmarkSort(); } - private static void test4() { - List values = new ArrayList<>(100); - for (int i = 0; i < 100; i++) { - UUID uuid = UUID.randomUUID(); - values.add(uuid.toString()); - } + // ✅ 1. Show available parallelism + private static void showParallelism() { + ForkJoinPool pool = ForkJoinPool.commonPool(); + System.out.println("Parallelism level: " + pool.getParallelism()); + } + + // ✅ 2. Sequential processing (baseline) + private static void processSequential(List strings) { + List result = strings.stream() + .filter(s -> s.startsWith("a")) + .map(String::toUpperCase) + .sorted() + .toList(); + + System.out.println("Sequential: " + result); + } + + // ✅ 3. Parallel processing + private static void processParallel(List strings) { + List result = strings.parallelStream() + .filter(s -> s.startsWith("a")) + .map(String::toUpperCase) + .sorted() + .toList(); + + System.out.println("Parallel: " + result); + } - // sequential + // ✅ 4. Proper benchmark + private static void benchmarkSort() { + List values = new ArrayList<>(100_000); + for (int i = 0; i < 100_000; i++) { + values.add(UUID.randomUUID().toString()); + } + + // Sequential benchmark long t0 = System.nanoTime(); - long count = values - .parallelStream() - .sorted((s1, s2) -> { - System.out.format("sort: %s <> %s [%s]\n", s1, s2, Thread.currentThread().getName()); - return s1.compareTo(s2); - }) + long count1 = values.stream() + .sorted() .count(); - System.out.println(count); long t1 = System.nanoTime(); - long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); - System.out.println(String.format("parallel sort took: %d ms", millis)); - } + // Parallel benchmark + long t2 = System.nanoTime(); - private static void test3(List strings) { - strings - .parallelStream() - .filter(s -> { - System.out.format("filter: %s [%s]\n", s, Thread.currentThread().getName()); - return true; - }) - .map(s -> { - System.out.format("map: %s [%s]\n", s, Thread.currentThread().getName()); - return s.toUpperCase(); - }) - .sorted((s1, s2) -> { - System.out.format("sort: %s <> %s [%s]\n", s1, s2, Thread.currentThread().getName()); - return s1.compareTo(s2); - }) - .forEach(s -> System.out.format("forEach: %s [%s]\n", s, Thread.currentThread().getName())); - } + long count2 = values.parallelStream() + .sorted() + .count(); - private static void test2(List strings) { - strings - .parallelStream() - .filter(s -> { - System.out.format("filter: %s [%s]\n", s, Thread.currentThread().getName()); - return true; - }) - .map(s -> { - System.out.format("map: %s [%s]\n", s, Thread.currentThread().getName()); - return s.toUpperCase(); - }) - .forEach(s -> System.out.format("forEach: %s [%s]\n", s, Thread.currentThread().getName())); - } + long t3 = System.nanoTime(); + + System.out.println("Sequential count: " + count1); + System.out.println("Parallel count: " + count2); - private static void test1() { - // -Djava.util.concurrent.ForkJoinPool.common.parallelism=5 + System.out.println("Sequential sort took: " + + TimeUnit.NANOSECONDS.toMillis(t1 - t0) + " ms"); - ForkJoinPool commonPool = ForkJoinPool.commonPool(); - System.out.println(commonPool.getParallelism()); + System.out.println("Parallel sort took: " + + TimeUnit.NANOSECONDS.toMillis(t3 - t2) + " ms"); } -} +} \ No newline at end of file diff --git a/src/com/winterbe/java8/samples/stream/Streams5.java b/src/com/winterbe/java8/samples/stream/Streams5.java index 560becf9..16cd69a1 100644 --- a/src/com/winterbe/java8/samples/stream/Streams5.java +++ b/src/com/winterbe/java8/samples/stream/Streams5.java @@ -2,137 +2,84 @@ import java.util.Arrays; import java.util.List; -import java.util.function.Supplier; import java.util.stream.Stream; /** - * Testing the order of execution. - * - * @author Benjamin Winterberg + * Optimized version of Streams demo. */ public class Streams5 { - public static void main(String[] args) { - List strings = - Arrays.asList("d2", "a2", "b1", "b3", "c"); - -// test1(strings); -// test2(strings); -// test3(strings); -// test4(strings); -// test5(strings); -// test6(strings); -// test7(strings); - test8(strings); - } + private static final String PREFIX = "a"; - private static void test8(List stringCollection) { - Supplier> streamSupplier = - () -> stringCollection - .stream() - .filter(s -> s.startsWith("a")); + public static void main(String[] args) { + List strings = Arrays.asList("d2", "a2", "b1", "b3", "c"); - streamSupplier.get().anyMatch(s -> true); - streamSupplier.get().noneMatch(s -> true); + basicFilter(strings); + mapThenFilter(strings); + filterThenMap(strings); + optimizedProcessing(strings); + shortCircuitExample(strings); + streamReuseFix(strings); } - // stream has already been operated upon or closed - private static void test7(List stringCollection) { - Stream stream = stringCollection - .stream() - .filter(s -> s.startsWith("a")); - - stream.anyMatch(s -> true); - stream.noneMatch(s -> true); + // ✅ Utility method (reusable logic) + private static boolean startsWithIgnoreCase(String s) { + return s.toLowerCase().startsWith(PREFIX); } - // short-circuit - private static void test6(List stringCollection) { - stringCollection - .stream() - .map(s -> { - System.out.println("map: " + s); - return s.toUpperCase(); - }) - .anyMatch(s -> { - System.out.println("anyMatch: " + s); - return s.startsWith("A"); - }); + // ✅ 1. Basic filter + private static void basicFilter(List list) { + list.stream() + .filter(s -> true) + .forEach(System.out::println); } - private static void test5(List stringCollection) { - stringCollection - .stream() - .filter(s -> { - System.out.println("filter: " + s); - return s.toLowerCase().startsWith("a"); - }) - .sorted((s1, s2) -> { - System.out.printf("sort: %s; %s\n", s1, s2); - return s1.compareTo(s2); - }) - .map(s -> { - System.out.println("map: " + s); - return s.toUpperCase(); - }) - .forEach(s -> System.out.println("forEach: " + s)); + // ✅ 2. Map then filter (less efficient) + private static void mapThenFilter(List list) { + list.stream() + .map(String::toUpperCase) + .filter(s -> s.startsWith("A")) + .forEach(System.out::println); } - // sorted = horizontal - private static void test4(List stringCollection) { - stringCollection - .stream() - .sorted((s1, s2) -> { - System.out.printf("sort: %s; %s\n", s1, s2); - return s1.compareTo(s2); - }) - .filter(s -> { - System.out.println("filter: " + s); - return s.toLowerCase().startsWith("a"); - }) - .map(s -> { - System.out.println("map: " + s); - return s.toUpperCase(); - }) - .forEach(s -> System.out.println("forEach: " + s)); + // ✅ 3. Filter then map (better) + private static void filterThenMap(List list) { + list.stream() + .filter(s -> s.startsWith("a")) + .map(String::toUpperCase) + .forEach(System.out::println); } - private static void test3(List stringCollection) { - stringCollection - .stream() - .filter(s -> { - System.out.println("filter: " + s); - return s.startsWith("a"); - }) - .map(s -> { - System.out.println("map: " + s); - return s.toUpperCase(); - }) - .forEach(s -> System.out.println("forEach: " + s)); + // ✅ 4. Optimized pipeline (best practice) + private static void optimizedProcessing(List list) { + list.stream() + .filter(Streams5::startsWithIgnoreCase) // filter first + .sorted() // then sort + .map(String::toUpperCase) // method reference + .forEach(System.out::println); } - private static void test2(List stringCollection) { - stringCollection - .stream() + // ✅ 5. Short-circuit example + private static void shortCircuitExample(List list) { + list.stream() .map(s -> { - System.out.println("map: " + s); + System.out.println("map: " + s); return s.toUpperCase(); }) - .filter(s -> { - System.out.println("filter: " + s); + .anyMatch(s -> { + System.out.println("anyMatch: " + s); return s.startsWith("A"); - }) - .forEach(s -> System.out.println("forEach: " + s)); + }); } - private static void test1(List stringCollection) { - stringCollection - .stream() - .filter(s -> { - System.out.println("filter: " + s); - return true; - }) - .forEach(s -> System.out.println("forEach: " + s)); + // ❌ Streams cannot be reused — fixed version + private static void streamReuseFix(List list) { + createStream(list).anyMatch(s -> true); + createStream(list).noneMatch(s -> true); } + // ✅ Stream factory method (instead of Supplier) + private static Stream createStream(List list) { + return list.stream().filter(s -> s.startsWith(PREFIX)); + } } \ No newline at end of file