From acc2aed0f225208d609216b9782f1844cf26f708 Mon Sep 17 00:00:00 2001 From: durgeshkumar Date: Thu, 4 Dec 2025 10:29:07 +0530 Subject: [PATCH 1/2] new: added searching substrings with string[] with lambdas --- java8/PROBLEMS.md | 40 +++++++-- java8/README.md | 41 +++------ .../PrintStringsComparison.java | 86 +++++++++++++++++++ .../Q005_print_string/PrintStringsStream.java | 35 ++++++++ .../PrintStringsTraditional.java | 30 +++++++ .../guide/java8/util/ComparisonUtils.java | 43 ++++++++++ 6 files changed, 241 insertions(+), 34 deletions(-) create mode 100644 java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsComparison.java create mode 100644 java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsStream.java create mode 100644 java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsTraditional.java diff --git a/java8/PROBLEMS.md b/java8/PROBLEMS.md index 36600ef..1b2d6e9 100644 --- a/java8/PROBLEMS.md +++ b/java8/PROBLEMS.md @@ -33,6 +33,10 @@ java8/ │ │ ├── MaxMinNumberStream.java # ✅ Complete │ │ ├── MaxMinNumberTraditional.java # ✅ Complete │ │ └── Pair.java # ✅ Helper class +│ ├── Q005_print_string/ +│ │ ├── PrintStringsComparison.java # ✅ Complete +│ │ ├── PrintStringsStream.java # ✅ Complete +│ │ └── PrintStringsTraditional.java # ✅ Complete │ └── util/ │ └── ComparisonUtils.java # ✅ Shared utilities └── target/ # Compiled output (not tracked in git) @@ -88,6 +92,19 @@ java8/ --- +### Q005: Filter Strings by Substring +**Location:** `com.modernjava.guide.java8.Q005_print_string` +**Concepts:** filter(), contains(), String operations, forEach() +**Problem:** Print all strings containing a specific substring (e.g., "_test") +**Input:** `["Apple_test", "Banana", "Cherry_test", "Date", "Elderberry", "Fig_test"]` +**Output:** `Apple_test, Cherry_test, Fig_test` +**Traditional:** ✅ `PrintStringsTraditional.java` - for loop with contains() check +**Stream:** ✅ `PrintStringsStream.java` - `filter(n -> n.contains(substring))` +**Comparison:** ✅ `PrintStringsComparison.java` - tests with 20, 1K, 100K, 1M strings +**Note:** Large array outputs are suppressed for performance testing + +--- + ## 🛠️ Utility Classes ### ComparisonUtils @@ -97,8 +114,11 @@ java8/ **Methods:** - `repeat(String str, int count)` - Repeats string N times (Java 8 compatible alternative to String.repeat()) - `formatTime(long nanos)` - Formats nanoseconds to human-readable format (ns, μs, ms) -- `printNumbers(int[] numbers)` - Prints array elements comma-separated +- `printNumbers(int[] numbers)` - Prints integer array elements comma-separated +- `printStrings(String[] strings)` - Prints string array elements comma-separated - `getArrayOfSpecifiedSize(int size)` - Generates random int array with positive, negative, and zero values (range: -100 to +100) +- `getStringArrayWithPatternAtEnd(int size, String pattern)` - Generates random string array from sample pool, randomly appending pattern to some strings +- `getStringArray()` - Returns the predefined string samples array --- @@ -108,7 +128,8 @@ java8/ - **Terminal Operations:** `forEach()`, `count()`, `sum()`, `max()`, `min()` - **Intermediate Operations:** `filter()`, `map()` - **Method References:** `System.out::println`, `Integer::compare` -- **Lambda Expressions:** `n -> n % 2 == 0`, `n -> n * n` +- **Lambda Expressions:** `n -> n % 2 == 0`, `n -> n * n`, `n -> n.contains(substring)` +- **String Operations:** `contains()`, filtering by substring ### Comparison Topics - Traditional vs Stream syntax @@ -116,13 +137,15 @@ java8/ - Code readability and maintainability - When to use each approach - Handling edge cases (empty arrays, single elements) +- Output suppression for large datasets in performance testing ### Java 8 Features Demonstrated - ✅ Lambda expressions - ✅ Method references -- ✅ Stream API basics +- ✅ Stream API basics (int and object streams) - ✅ Functional interfaces - ✅ Optional (in max/min operations) +- ✅ String filtering and predicates --- @@ -132,6 +155,7 @@ Based on benchmark results in comparison classes: - **Small arrays (< 100 elements):** Traditional approach is faster due to Stream overhead - **Large arrays (10,000+ elements):** Stream performance improves, gap narrows - **Very large arrays (1,000,000+ elements):** Traditional still edges out for simple operations +- **String operations:** Similar patterns - traditional loops excel for simple contains() checks - **Takeaway:** Use Streams for readability and composition, not raw speed in tight loops --- @@ -139,16 +163,18 @@ Based on benchmark results in comparison classes: ## 🚧 Planned Problems ### Easy -- Q003: Print even numbers only -- Q004: Find sum of all numbers -- Q005: Find max/min element - Q006: Count even vs odd numbers -- Q007: Remove duplicates +- Q007: Remove duplicates from array +- Q008: Convert all strings to uppercase +- Q009: Find average of numbers +- Q010: Check if any element matches a condition ### Medium - Q011: Group strings by length - Q012: Find top K frequent elements - Q013: Flatten nested lists +- Q014: Partition numbers into even/odd lists +- Q015: Find first element matching condition - Q014: Partition list into even/odd ### Hard diff --git a/java8/README.md b/java8/README.md index e67499b..284c0ed 100644 --- a/java8/README.md +++ b/java8/README.md @@ -29,6 +29,7 @@ java8/ │ ├── Q002_print_evens/ # ✅ Filter even numbers │ ├── Q003_square_nums/ # ✅ Sum of squares │ ├── Q004_max_min/ # ✅ Find max & min +│ ├── Q005_print_string/ # ✅ Filter strings by substring │ └── util/ │ └── ComparisonUtils.java # ✅ Shared utilities └── target/ # Compiled output (gitignored) @@ -62,6 +63,9 @@ mvn -pl java8 exec:java -Dexec.mainClass="com.modernjava.guide.java8.Q003_square # Run Q004: Max & Min mvn -pl java8 exec:java -Dexec.mainClass="com.modernjava.guide.java8.Q004_max_min.MaxMinNumberComparison" + +# Run Q005: Filter Strings +mvn -pl java8 exec:java -Dexec.mainClass="com.modernjava.guide.java8.Q005_print_string.PrintStringsComparison" ``` --- @@ -106,14 +110,15 @@ mvn -pl java8 exec:java -Dexec.mainClass="com.modernjava.guide.java8.Q004_max_mi ## 📊 Current Status -### Completed Problems (4/4) +### Completed Problems (5/5) - ✅ **Q001:** Print All Numbers - Stream basics, forEach(), method references - ✅ **Q002:** Print Even Numbers - filter(), predicates - ✅ **Q003:** Sum of Squares - map(), sum(), transformations - ✅ **Q004:** Max & Min - max(), min(), Optional handling +- ✅ **Q005:** Filter Strings by Substring - filter(), contains(), String operations ### Utility Classes -- ✅ **ComparisonUtils** - Shared helpers (repeat, formatTime, printNumbers, getArrayOfSpecifiedSize) +- ✅ **ComparisonUtils** - Shared helpers (repeat, formatTime, printNumbers, printStrings, getArrayOfSpecifiedSize, getStringArrayWithPatternAtEnd) See **[PROBLEMS.md](PROBLEMS.md)** for detailed problem statements and concepts covered. @@ -127,11 +132,13 @@ Each problem demonstrates: 3. **Comparison** - Side-by-side execution with performance metrics ### Key Concepts Covered -- Lambda expressions: `n -> n % 2 == 0` +- Lambda expressions: `n -> n % 2 == 0`, `n -> n.contains(substring)` - Method references: `System.out::println` - Stream operations: `filter()`, `map()`, `forEach()`, `sum()`, `max()`, `min()` -- Performance analysis: nano-time benchmarks with multiple test cases +- String operations: `contains()`, filtering by substring +- Performance analysis: nano-time benchmarks with multiple test cases (small to 1M+ elements) - Edge cases: empty arrays, single elements, large datasets +- Output suppression for large dataset performance testing --- @@ -179,7 +186,9 @@ See **[Troubleshoot.md](Troubleshoot.md)** for complete error reference. - This module uses `1.8` and `1.8` configuration - All utility methods are Java 8 compatible (no String.repeat(), no var, etc.) - Performance benchmarks include JIT warmup considerations -- Random array generation uses range -100 to +100 (positive, negative, zero values) +- Random int array generation uses range -100 to +100 (positive, negative, zero values) +- Random string array generation uses predefined sample pool with configurable pattern matching +- Large array outputs (size > 100) are suppressed during comparison tests for readability --- @@ -187,28 +196,6 @@ See **[Troubleshoot.md](Troubleshoot.md)** for complete error reference. - [Root README](../README.md) - Multi-module project overview - [Git Best Practices](../docs/GIT_BEST_PRACTICES.md) - Contribution guidelines - [Troubleshooting](../docs/TROUBLESHOOTING.md) - Global error reference -1. **Traditional Solution** - Pre-Java 8 imperative style with loops -2. **Stream Solution** - Java 8+ functional style with Stream API -3. **Comparison Runner** - Side-by-side execution with performance metrics - -Example problems: -- **Q001: Print Numbers** - forEach(), method references, lambda expressions (WORKING) -- **Q002: Sum of Squares** - filter(), map(), reduce() (INCOMPLETE - needs Stream version) - -**For detailed structure:** See [LEARNING_STRUCTURE.md](LEARNING_STRUCTURE.md) - ---- - -## Java 8 Features & Resources - -Features newly included within Java 8: https://www.oracle.com/java/technologies/javase/8-whats-new.html - -Key topics to explore: -- **Stream API**: https://docs.oracle.com/javase/tutorial/collections/streams/ -- **Lambda Expressions**: Functional interfaces and method references -- **Optional**: Better null handling -- **Date/Time API**: Modern date and time handling -- **Default Methods**: Interface evolution Additional resources: - Java learning path: https://docs.oracle.com/javase/tutorial/tutorialLearningPaths.html diff --git a/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsComparison.java b/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsComparison.java new file mode 100644 index 0000000..2aa5c19 --- /dev/null +++ b/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsComparison.java @@ -0,0 +1,86 @@ +package com.modernjava.guide.java8.Q005_print_string; + +import com.modernjava.guide.java8.util.ComparisonUtils; + +/** + * Q005: Print all strings containing a specific substring + * Compares Traditional approach vs Stream API approach + * Tests with small, medium, and large datasets + */ +public class PrintStringsComparison { + public static void main(String[] args) { + String substring = "_test"; + + System.out.println("Q005: Print All Strings containing substring '_test' using Both Approaches"); + System.out.println(ComparisonUtils.repeat("=", 80)); + + // Test Case 1: Small dataset (20 strings) + System.out.println("\n" + ComparisonUtils.repeat("-", 30) + " Test Case 1: Small Dataset " + ComparisonUtils.repeat("-", 30)); + String[] strings = ComparisonUtils.getStringArrayWithPatternAtEnd(20, substring); + System.out.println("Input (size=" + strings.length + "): "); + ComparisonUtils.printStrings(strings); + compareApproaches(strings, substring, 20); + + // Test Case 2: Medium dataset (1,000 strings) + System.out.println("\n\n" + ComparisonUtils.repeat("-", 30) + " Test Case 2: Medium Dataset " + ComparisonUtils.repeat("-", 30)); + strings = ComparisonUtils.getStringArrayWithPatternAtEnd(1_000, substring); + System.out.println("Input (size=" + strings.length + "): "); + System.out.println("[... 1,000 strings ...]"); + compareApproaches(strings, substring, 1_000); + + // Test Case 3: Large dataset (100,000 strings) + System.out.println("\n\n" + ComparisonUtils.repeat("-", 30) + " Test Case 3: Large Dataset " + ComparisonUtils.repeat("-", 30)); + strings = ComparisonUtils.getStringArrayWithPatternAtEnd(100_000, substring); + System.out.println("Input (size=" + strings.length + "): "); + System.out.println("[... 100,000 strings ...]"); + compareApproaches(strings, substring, 100_000); + + // Test Case 4: Very Large dataset (1,000,000 strings) + System.out.println("\n\n" + ComparisonUtils.repeat("-", 30) + " Test Case 4: Very Large Dataset " + ComparisonUtils.repeat("-", 30)); + strings = ComparisonUtils.getStringArrayWithPatternAtEnd(1_000_000, substring); + System.out.println("Input (size=" + strings.length + "): "); + System.out.println("[... 1,000,000 strings ...]"); + compareApproaches(strings, substring, 1_000_000); + } + + private static void compareApproaches(String[] strings, String substring, int size) { + // Traditional Approach + System.out.println("\nAPPROACH: Traditional"); + System.out.println(ComparisonUtils.repeat("-", 80)); + long startTime = System.nanoTime(); + + if (size > 50) { + System.out.println("[Skipped printing output for large array of size " + size + "]"); + } else { + PrintStringsTraditional.printStrings(strings, substring); + } + + long endTime = System.nanoTime(); + long traditionalTime = endTime - startTime; + System.out.println("Time: " + traditionalTime + " ns (" + ComparisonUtils.formatTime(traditionalTime) + ")"); + + // Stream API Approach + System.out.println("\nAPPROACH: Stream API"); + System.out.println(ComparisonUtils.repeat("-", 80)); + long streamStartTime = System.nanoTime(); + PrintStringsStream.searchStrings(strings, substring); + + + long streamEndTime = System.nanoTime(); + long streamTime = streamEndTime - streamStartTime; + System.out.println("Time: " + streamTime + " ns (" + ComparisonUtils.formatTime(streamTime) + ")"); + + // Performance Comparison + System.out.println("\n" + ComparisonUtils.repeat("-", 80)); + System.out.println("PERFORMANCE COMPARISON:"); + System.out.println("Traditional: " + ComparisonUtils.formatTime(traditionalTime)); + System.out.println("Stream API: " + ComparisonUtils.formatTime(streamTime)); + if (traditionalTime < streamTime) { + double ratio = (double) streamTime / traditionalTime; + System.out.println("Traditional is " + String.format("%.2f", ratio) + "x faster"); + } else { + double ratio = (double) traditionalTime / streamTime; + System.out.println("Stream API is " + String.format("%.2f", ratio) + "x faster"); + } + } +} diff --git a/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsStream.java b/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsStream.java new file mode 100644 index 0000000..705d1f1 --- /dev/null +++ b/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsStream.java @@ -0,0 +1,35 @@ +package com.modernjava.guide.java8.Q005_print_string; + +import com.modernjava.guide.java8.util.ComparisonUtils; + +import java.util.Arrays; + +/** + * search strings containing a substring using Stream API + */ +public class PrintStringsStream { + + public static void main(String[] args) { + System.out.println("Q005: Print All Strings containing substring '_test' using Stream API"); + ComparisonUtils.repeat("-",80);; + String[] strings = ComparisonUtils.getStringArrayWithPatternAtEnd(40,"_test"); + //{"Apple_test", "Banana", "Cherry_test", "Date", "Elderberry", "Fig_test", "Grape", "Honeydew_test"}; + String subString = "_test"; + + System.out.println("Input with length "+strings.length+": "); + ComparisonUtils.printStrings(strings); + + System.out.println("Output: "); + searchStrings(strings, subString); + } + + + public static void searchStrings(String[] strings, String subString){ + Arrays.stream(strings) + .filter(n->n.contains(subString)) + .forEach(n->System.out.print(n+",")); + } + + + +} diff --git a/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsTraditional.java b/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsTraditional.java new file mode 100644 index 0000000..893dc9b --- /dev/null +++ b/java8/src/main/java/com/modernjava/guide/java8/Q005_print_string/PrintStringsTraditional.java @@ -0,0 +1,30 @@ +package com.modernjava.guide.java8.Q005_print_string; + +import com.modernjava.guide.java8.util.ComparisonUtils; + +public class PrintStringsTraditional { + public static void main(String[] args) { + String substring = "_test"; + String[] strings = ComparisonUtils.getStringArrayWithPatternAtEnd(20,substring); + System.out.println("Q005: Print All Strings containing substring '_test' using Traditional Approach"); + System.out.println(ComparisonUtils.repeat("-",80));; + System.out.println("Input: "); + ComparisonUtils.printStrings(strings); + System.out.println("Output: "); + printStrings(strings, substring); + } + + public static void printStrings(String[] strings, String substring) { + /** + * Traditional approach to print all strings in an array. + * Steps: + * 1. Use a for-each loop to iterate through each string in the array. + * 2. Print each string to the console. + */ + for (String str : strings) { + if(str.contains(substring)) + System.out.print(str+","); + } + System.out.println("\n"); + } +} diff --git a/java8/src/main/java/com/modernjava/guide/java8/util/ComparisonUtils.java b/java8/src/main/java/com/modernjava/guide/java8/util/ComparisonUtils.java index 66d8e88..6dff48f 100644 --- a/java8/src/main/java/com/modernjava/guide/java8/util/ComparisonUtils.java +++ b/java8/src/main/java/com/modernjava/guide/java8/util/ComparisonUtils.java @@ -8,6 +8,15 @@ */ public class ComparisonUtils { + private static final String[] stringSamples= new String[]{ + "Apple_test", "Banana", "Cherry", "Date", "Elderberry", "Fig_test", "Grape", "Honeydew", + "Kiwi", "Lemon_test", "Mango", "Nectarine", "Orange", "Papaya", "Quince", "Raspberry", + "Strawberry", "Tangerine", "UgliFruit", "Vanilla", "Watermelon", "Xigua", "Yam", "Zucchini", + "Apple","Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape", "Honeydew", "Chocolate", "IceCream", + "Pineapple_test", "Coconut", "Blueberry_test", "Blackberry", "Cranberry", "Dragonfruit", "Jackfruit", "Durian", "Lychee", "Rambutan", + "Starfruit", "Persimmon", "Clementine", "Currant", "Gooseberry", "Huckleberry", "Salak", "Soursop", "Tamarind", "UvaUrsi", "Ziziphus" + }; + // Private constructor to prevent instantiation private ComparisonUtils() { throw new AssertionError("Utility class should not be instantiated"); @@ -74,5 +83,39 @@ public static int[] getArrayOfSpecifiedSize( int size) { } return result; } + + public static void printStrings(String[] strings) { + System.out.print("["); + for (String str : strings) { + System.out.print(str + ","); + } + System.out.println("]\n"); + } + + public static String[] getStringArrayWithPatternAtEnd(int size, String pattern) { + if (size <= 0) { + return new String[0]; + } + Random rand = new Random(); + // Generate an array of strings with some containing the specified pattern + String[] strings = new String[size]; + for (int i = 0; i < size; i++) { + // Get a random index within stringSamples bounds + int randomIndex = rand.nextInt(stringSamples.length); + String baseString = stringSamples[randomIndex]; + + // making it more random + if (i % 5 == 0 && rand.nextBoolean()) { + strings[i] = baseString + pattern; + } else { + strings[i] = baseString; + } + } + return strings; + } + + public static String[] getStringArray() { + return ComparisonUtils.stringSamples; + } } From 28eab84b699233b5fbae32cd88fd15f935e1206c Mon Sep 17 00:00:00 2001 From: durgeshkumar Date: Thu, 4 Dec 2025 10:56:31 +0530 Subject: [PATCH 2/2] new: count evens odd within same array --- java8/PROBLEMS.md | 22 +++- java8/README.md | 13 +- .../CountEvensOddsComparison.java | 121 ++++++++++++++++++ 3 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 java8/src/main/java/com/modernjava/guide/java8/Q006_count_evens_odds/CountEvensOddsComparison.java diff --git a/java8/PROBLEMS.md b/java8/PROBLEMS.md index 1b2d6e9..57cc300 100644 --- a/java8/PROBLEMS.md +++ b/java8/PROBLEMS.md @@ -37,6 +37,8 @@ java8/ │ │ ├── PrintStringsComparison.java # ✅ Complete │ │ ├── PrintStringsStream.java # ✅ Complete │ │ └── PrintStringsTraditional.java # ✅ Complete +│ ├── Q006_count_evens_odds/ +│ │ └── CountEvensOddsComparison.java # ✅ Complete (Single class) │ └── util/ │ └── ComparisonUtils.java # ✅ Shared utilities └── target/ # Compiled output (not tracked in git) @@ -105,6 +107,21 @@ java8/ --- +### Q006: Count Even and Odd Numbers +**Location:** `com.modernjava.guide.java8.Q006_count_evens_odds` +**Concepts:** count(), filter(), predicates, single-pass iteration +**Problem:** Count how many even and odd numbers are in an array +**Input:** `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]` +**Output:** `Even count: 5, Odd count: 5` +**Implementation:** ✅ `CountEvensOddsComparison.java` - **Single class with both approaches** +**Methods:** + - `countTraditional()` - Single for loop counting both even/odd + - `countStream()` - Two separate stream pipelines with filter().count() +**Test Cases:** 10, 1K, 100K elements +**Note:** This demonstrates the **single-class approach** for simple problems - all code in one file + +--- + ## 🛠️ Utility Classes ### ComparisonUtils @@ -138,6 +155,7 @@ java8/ - When to use each approach - Handling edge cases (empty arrays, single elements) - Output suppression for large datasets in performance testing +- **Single-class approach** for simple problems (Q006+) ### Java 8 Features Demonstrated - ✅ Lambda expressions @@ -146,6 +164,7 @@ java8/ - ✅ Functional interfaces - ✅ Optional (in max/min operations) - ✅ String filtering and predicates +- ✅ Counting with filter().count() --- @@ -156,6 +175,7 @@ Based on benchmark results in comparison classes: - **Large arrays (10,000+ elements):** Stream performance improves, gap narrows - **Very large arrays (1,000,000+ elements):** Traditional still edges out for simple operations - **String operations:** Similar patterns - traditional loops excel for simple contains() checks +- **Counting operations:** Traditional single-pass is more efficient than multiple stream pipelines - **Takeaway:** Use Streams for readability and composition, not raw speed in tight loops --- @@ -163,11 +183,11 @@ Based on benchmark results in comparison classes: ## 🚧 Planned Problems ### Easy -- Q006: Count even vs odd numbers - Q007: Remove duplicates from array - Q008: Convert all strings to uppercase - Q009: Find average of numbers - Q010: Check if any element matches a condition +- Q011: Sum all positive numbers ### Medium - Q011: Group strings by length diff --git a/java8/README.md b/java8/README.md index 284c0ed..24121cf 100644 --- a/java8/README.md +++ b/java8/README.md @@ -30,6 +30,7 @@ java8/ │ ├── Q003_square_nums/ # ✅ Sum of squares │ ├── Q004_max_min/ # ✅ Find max & min │ ├── Q005_print_string/ # ✅ Filter strings by substring +│ ├── Q006_count_evens_odds/ # ✅ Count even/odd (single class) │ └── util/ │ └── ComparisonUtils.java # ✅ Shared utilities └── target/ # Compiled output (gitignored) @@ -66,6 +67,9 @@ mvn -pl java8 exec:java -Dexec.mainClass="com.modernjava.guide.java8.Q004_max_mi # Run Q005: Filter Strings mvn -pl java8 exec:java -Dexec.mainClass="com.modernjava.guide.java8.Q005_print_string.PrintStringsComparison" + +# Run Q006: Count Even/Odd +mvn -pl java8 exec:java -Dexec.mainClass="com.modernjava.guide.java8.Q006_count_evens_odds.CountEvensOddsComparison" ``` --- @@ -110,12 +114,13 @@ mvn -pl java8 exec:java -Dexec.mainClass="com.modernjava.guide.java8.Q005_print_ ## 📊 Current Status -### Completed Problems (5/5) +### Completed Problems (6/6) - ✅ **Q001:** Print All Numbers - Stream basics, forEach(), method references - ✅ **Q002:** Print Even Numbers - filter(), predicates - ✅ **Q003:** Sum of Squares - map(), sum(), transformations - ✅ **Q004:** Max & Min - max(), min(), Optional handling - ✅ **Q005:** Filter Strings by Substring - filter(), contains(), String operations +- ✅ **Q006:** Count Even/Odd Numbers - count(), filter() (single-class approach) ### Utility Classes - ✅ **ComparisonUtils** - Shared helpers (repeat, formatTime, printNumbers, printStrings, getArrayOfSpecifiedSize, getStringArrayWithPatternAtEnd) @@ -126,6 +131,10 @@ See **[PROBLEMS.md](PROBLEMS.md)** for detailed problem statements and concepts ## 🎓 Learning Approach +**Two implementation patterns:** +1. **Multi-file approach (Q001-Q005):** Separate Traditional/Stream/Comparison classes +2. **Single-class approach (Q006+):** All methods in one comparison class - better for simple problems + Each problem demonstrates: 1. **Traditional Approach** - Classic Java loops and conditionals 2. **Stream Approach** - Java 8 Stream API with lambdas @@ -134,7 +143,7 @@ Each problem demonstrates: ### Key Concepts Covered - Lambda expressions: `n -> n % 2 == 0`, `n -> n.contains(substring)` - Method references: `System.out::println` -- Stream operations: `filter()`, `map()`, `forEach()`, `sum()`, `max()`, `min()` +- Stream operations: `filter()`, `map()`, `forEach()`, `sum()`, `max()`, `min()`, `count()` - String operations: `contains()`, filtering by substring - Performance analysis: nano-time benchmarks with multiple test cases (small to 1M+ elements) - Edge cases: empty arrays, single elements, large datasets diff --git a/java8/src/main/java/com/modernjava/guide/java8/Q006_count_evens_odds/CountEvensOddsComparison.java b/java8/src/main/java/com/modernjava/guide/java8/Q006_count_evens_odds/CountEvensOddsComparison.java new file mode 100644 index 0000000..7a5905e --- /dev/null +++ b/java8/src/main/java/com/modernjava/guide/java8/Q006_count_evens_odds/CountEvensOddsComparison.java @@ -0,0 +1,121 @@ +package com.modernjava.guide.java8.Q006_count_evens_odds; + +import com.modernjava.guide.java8.util.ComparisonUtils; +import java.util.Arrays; + +/** + * Q006: Count Even and Odd Numbers + * Demonstrates both Traditional and Stream approaches in a single class + */ +public class CountEvensOddsComparison { + + public static void main(String[] args) { + System.out.println("Q006: Count Even and Odd Numbers using Both Approaches"); + System.out.println(ComparisonUtils.repeat("=", 80)); + + // Test Case 1: Small dataset + System.out.println("\n" + ComparisonUtils.repeat("-", 30) + " Test Case 1: Small Dataset " + ComparisonUtils.repeat("-", 30)); + int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + System.out.println("Input (size=" + numbers.length + "): "); + ComparisonUtils.printNumbers(numbers); + compareApproaches(numbers); + + // Test Case 2: Medium dataset + System.out.println("\n\n" + ComparisonUtils.repeat("-", 30) + " Test Case 2: Medium Dataset " + ComparisonUtils.repeat("-", 30)); + numbers = ComparisonUtils.getArrayOfSpecifiedSize(1_000); + System.out.println("Input (size=" + numbers.length + "): [... 1,000 numbers ...]"); + compareApproaches(numbers); + + // Test Case 3: Large dataset + System.out.println("\n\n" + ComparisonUtils.repeat("-", 30) + " Test Case 3: Large Dataset " + ComparisonUtils.repeat("-", 30)); + numbers = ComparisonUtils.getArrayOfSpecifiedSize(100_000); + System.out.println("Input (size=" + numbers.length + "): [... 100,000 numbers ...]"); + compareApproaches(numbers); + } + + /** + * Traditional approach to count even and odd numbers + */ + public static Result countTraditional(int[] numbers) { + int evenCount = 0; + int oddCount = 0; + + for (int number : numbers) { + if (number % 2 == 0) { + evenCount++; + } else { + oddCount++; + } + } + + return new Result(evenCount, oddCount); + } + + /** + * Stream API approach to count even and odd numbers + */ + public static Result countStream(int[] numbers) { + long evenCount = Arrays.stream(numbers) + .filter(n -> n % 2 == 0) + .count(); + + long oddCount = Arrays.stream(numbers) + .filter(n -> n % 2 != 0) + .count(); + + return new Result((int) evenCount, (int) oddCount); + } + + /** + * Compare both approaches with performance metrics + */ + private static void compareApproaches(int[] numbers) { + // Traditional Approach + System.out.println("\nAPPROACH: Traditional"); + System.out.println(ComparisonUtils.repeat("-", 80)); + long startTime = System.nanoTime(); + Result traditionalResult = countTraditional(numbers); + long endTime = System.nanoTime(); + long traditionalTime = endTime - startTime; + System.out.println("Even count: " + traditionalResult.evenCount); + System.out.println("Odd count: " + traditionalResult.oddCount); + System.out.println("Time: " + traditionalTime + " ns (" + ComparisonUtils.formatTime(traditionalTime) + ")"); + + // Stream API Approach + System.out.println("\nAPPROACH: Stream API"); + System.out.println(ComparisonUtils.repeat("-", 80)); + long streamStartTime = System.nanoTime(); + Result streamResult = countStream(numbers); + long streamEndTime = System.nanoTime(); + long streamTime = streamEndTime - streamStartTime; + System.out.println("Even count: " + streamResult.evenCount); + System.out.println("Odd count: " + streamResult.oddCount); + System.out.println("Time: " + streamTime + " ns (" + ComparisonUtils.formatTime(streamTime) + ")"); + + // Performance Comparison + System.out.println("\n" + ComparisonUtils.repeat("-", 80)); + System.out.println("PERFORMANCE COMPARISON:"); + System.out.println("Traditional: " + ComparisonUtils.formatTime(traditionalTime)); + System.out.println("Stream API: " + ComparisonUtils.formatTime(streamTime)); + if (traditionalTime < streamTime) { + double ratio = (double) streamTime / traditionalTime; + System.out.println("Traditional is " + String.format("%.2f", ratio) + "x faster"); + } else { + double ratio = (double) traditionalTime / streamTime; + System.out.println("Stream API is " + String.format("%.2f", ratio) + "x faster"); + } + } + + /** + * Helper class to hold the result of counting + */ + public static class Result { + final int evenCount; + final int oddCount; + + Result(int evenCount, int oddCount) { + this.evenCount = evenCount; + this.oddCount = oddCount; + } + } +}