From 72ec5b90a400338e5d947e7bf8b01054680fcaa0 Mon Sep 17 00:00:00 2001 From: Wizzac <13040359+wizzac@users.noreply.github.com> Date: Wed, 9 Oct 2024 23:05:51 -0300 Subject: [PATCH 1/2] First commit Adding implementation of mapReduce --- map-reduce/README.md | 124 ++++++++++++++++++ map-reduce/pom.xml | 67 ++++++++++ .../main/java/com/iluwatar/mapreduce/App.java | 80 +++++++++++ .../src/test/java/com/iluwatar/AppTest.java | 103 +++++++++++++++ pom.xml | 1 + 5 files changed, 375 insertions(+) create mode 100644 map-reduce/README.md create mode 100644 map-reduce/pom.xml create mode 100644 map-reduce/src/main/java/com/iluwatar/mapreduce/App.java create mode 100644 map-reduce/src/test/java/com/iluwatar/AppTest.java diff --git a/map-reduce/README.md b/map-reduce/README.md new file mode 100644 index 000000000000..6187608fa2be --- /dev/null +++ b/map-reduce/README.md @@ -0,0 +1,124 @@ +--- +title: "MapReduce Pattern in Java" +shortTitle: MapReduce +description: "Learn the MapReduce pattern in Java with real-world examples, class diagrams, and tutorials. Understand its intent, applicability, benefits, and known uses to enhance your design pattern knowledge." +category: Performance optimization +language: en +tag: + - Data processing + - Code simplification + - Delegation + - Performance +--- + +# MapReduce in Java + +## Intent of Map Reduce Pattern + +The MapReduce design pattern is intended to simplify the processing of large-scale data sets by breaking down complex tasks into smaller, manageable units of work. This pattern leverages parallel processing to increase efficiency, scalability, and fault tolerance across distributed systems. + +## Detailed Explanation of Map Reduce with Real-World Examples + +1. Map Phase + The Map phase is the first step in the process. It takes the input data (often unstructured or semi-structured) and transforms it into intermediate key-value pairs. Each data element is processed independently and converted into one or more key-value pairs. +2. Reduce Phase + The Reduce phase aggregates the intermediate key-value pairs produced in the Map phase. All values associated with the same key are passed to the Reduce function, which processes them to produce a final output. + +Real-world example + +> Imagine you work for a company that processes millions of customer reviews, and you want to find out how often each word is used across all the reviews. Doing this on one machine would take a lot of time, so you want to use a MapReduce process to speed it up by distributing the work across multiple computers. + +In plain words + +> By abstracting the distribution and coordination of these tasks, MapReduce enables developers to focus on the processing logic, rather than the complexities of managing concurrency + +Wikipedia say +> MapReduce is a programming model and an associated implementation for processing and generating big data sets with a parallel, distributed algorithm on a cluster. + +## Programmatic Example of DTO Pattern in Java + +Let's first start with our map method + +```java +public static List> map(List sentences) { + List> mapped = new ArrayList<>(); + for (String sentence : sentences) { + String[] words = sentence.split("\\s+"); + for (String word : words) { + mapped.add(new AbstractMap.SimpleEntry<>(word.toLowerCase(), 1)); + } + } + return mapped; +} +``` +The purpose of the Map function is to process the input data and convert it into key-value pairs. + +Now lets go with the reduce function: +```java + public static Map reduce(List> mappedWords) { + Map reduced = new HashMap<>(); + for (Map.Entry entry : mappedWords) { + reduced.merge(entry.getKey(), entry.getValue(), Integer::sum); + } + return reduced; + } +``` +In the Reduce phase, the grouped key-value pairs are processed to combine or summarize the data. The idea is to aggregate all the values for each key to produce the final result. + + + +An example of the console output after running the algorithm with the following input: +input +```java +List sentences = Arrays.asList( +"hello world", +"hello java java", +"map reduce pattern in java", +"world of java map reduce" +); +``` +Output +``` +reduce: 2 +java: 4 +world: 2 +in: 1 +of: 1 +pattern: 1 +hello: 2 +map: 2 +``` + +## When to Use the MapReduce Pattern in Java + +Use the MapReduce pattern when: + +* You are working with massive datasets that can't fit on a single machine. +* You need a batch-processing solution to analyze or transform large volumes of data. +* Fault tolerance and high availability are important for your use case. + +## MapReduce Pattern Java Tutorials + +* [MapReduce Algorithm (Baeldung)](https://www.baeldung.com/cs/mapreduce-algorithm) +* [MapReduce Tutorial (javatpoint)](https://www.javatpoint.com/mapreduce) + + +## Real-World Applications of MapReduce Pattern in Java +MapReduce is a powerful tool for processing large-scale data because it breaks down complex tasks into smaller, manageable parts, allowing for efficient parallel computation across distributed systems + +## Benefits of MapReduce pattern + +1. Developers only need to define two simple functions: Map and Reduce. They don't have to worry about how tasks are distributed, how failures are handled, or how the data is split. +2. MapReduce scales horizontally. You can add more machines (nodes) to the cluster to handle larger data sets or process more tasks in parallel. This makes it perfect for large datasets in the order of terabytes or petabytes. +3. The system is designed to handle failures. If a worker node crashes or a task fails, it automatically retries the task on another available node without developer intervention. + +## Trade-offs of Map Reduce pattern + +1. MapReduce is designed for batch processing, meaning it’s not suitable for real-time data processing or low-latency jobs. The process of splitting data, shuffling it between nodes, and reducing it can introduce significant delays. +2. If the dataset is small or can fit in memory on a single machine, MapReduce can be overkill. The overhead of distributing tasks across nodes outweighs the benefits for small-scale tasks. Single-node solutions like a relational database or in-memory processing tools. +3. MapReduce frequently reads from and writes to disk (between the Map and Reduce phases), leading to performance bottlenecks. This is particularly problematic for jobs with lots of intermediate data. + +## References and Credits + +* [Designing Data-intensive Applications](https://www.amazon.com/Designing-Data-Intensive-Applications-Reliable-Maintainable/dp/1449373321/ref=sr_1_1?s=books&sr=1-1) +* [MapReduce Design Patterns](https://www.amazon.com/MapReduce-Design-Patterns-Effective-Algorithms/dp/1449327176/ref=sr_1_1?crid=3N6I3219DQBM&dib=eyJ2IjoiMSJ9.v6J5LaH30wtWyGQ7t20oSWIhd3rZs9GOaU3r-fSfZbd11rwjP0d0lL4tdcsD_yMt-WY6-XDWWakgkvMv38W9YD7CZDIgJ1G-LuazC8rNILObJBIRg09-7-ugQHZbtkqZFEt1ZCyFiDV4E3Iq2Db41vOpjbrU_B-phwzNQoRU175m1i-WvzTdcWL5GwVcbIWClmYB99kszZ1wX76nfjfq9YUHAFZtlpvLNMavBY4KTjI.QhcDrdrN5Bdd5ZVRTf9cZw0lAXNX83ncVVws8UbVDKU&dib_tag=se&keywords=MapReduce+Design+Patterns&qid=1728522338&s=books&sprefix=mapreduce+design+patterns%2Cstripbooks-intl-ship%2C198&sr=1-1) \ No newline at end of file diff --git a/map-reduce/pom.xml b/map-reduce/pom.xml new file mode 100644 index 000000000000..43ee4bf78fd7 --- /dev/null +++ b/map-reduce/pom.xml @@ -0,0 +1,67 @@ + + + + + java-design-patterns + com.iluwatar + 1.26.0-SNAPSHOT + + 4.0.0 + marker-interface + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.hamcrest + hamcrest-core + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + App + + + + + + + + + diff --git a/map-reduce/src/main/java/com/iluwatar/mapreduce/App.java b/map-reduce/src/main/java/com/iluwatar/mapreduce/App.java new file mode 100644 index 000000000000..117247834083 --- /dev/null +++ b/map-reduce/src/main/java/com/iluwatar/mapreduce/App.java @@ -0,0 +1,80 @@ +package com.iluwatar.mapreduce; + +import lombok.extern.slf4j.Slf4j; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * The main intent of the MapReduce design pattern is to allow for the processing of large data sets with a distributed algorithm, + * minimizing the overall time of computation by exploiting various parallel computing nodes. This design pattern simplifies the complexity + * of concurrency and hides the details of data distribution,fault tolerance, and load balancing, making it an effective model for + * processing vast amounts of data. + */ +@Slf4j +public class App { + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + + // Sample input: List of sentences + List sentences = Arrays.asList( + "hello world", + "hello java java", + "map reduce pattern in java", + "world of java map reduce" + ); + + // Step 1: Map phase + List> mappedWords = map(sentences); + + // Step 2: Reduce phase + Map wordCounts = reduce(mappedWords); + + // Step 3: Output the final result + wordCounts.forEach((word, count) -> LOGGER.info("{}: {}", word, count)); + } + + /** + * The map function processes a list of input data and produces key-value pairs. + * + * @param sentences The input data to be processed by the map function. + * @return A List of maps entries containing keys (e.g., words) and their occurrences. + */ + public static List> map(List sentences) { + List> mapped = new ArrayList<>(); + for (String sentence : sentences) { + // Split the sentence into words using whitespace as a delimiter + String[] words = sentence.split("\\s+"); + for (String word : words) { + // Create a key-value pair where the key is the word and the value is 1 + mapped.add(new AbstractMap.SimpleEntry<>(word.toLowerCase(), 1)); + } + } + return mapped; + } + + /** + * The reduce function processes the grouped data and aggregates the values + * (e.g., sums up the occurrences for each word). + * + * @param mappedWords A List of maps where each key has a list of associated values. + * @return A final map with each key and its aggregated result. + */ + public static Map reduce(List> mappedWords) { + Map reduced = new HashMap<>(); + for (Map.Entry entry : mappedWords) { + // If the word is already in the map, increment the count, otherwise set it to 1 + reduced.merge(entry.getKey(), entry.getValue(), Integer::sum); + } + return reduced; + } + +} \ No newline at end of file diff --git a/map-reduce/src/test/java/com/iluwatar/AppTest.java b/map-reduce/src/test/java/com/iluwatar/AppTest.java new file mode 100644 index 000000000000..a6ec19f1cd88 --- /dev/null +++ b/map-reduce/src/test/java/com/iluwatar/AppTest.java @@ -0,0 +1,103 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar; + +import com.iluwatar.mapreduce.App; +import org.junit.jupiter.api.Test; + +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AppTest { + + @Test + void testMap_singleSentence() { + + List sentences = Arrays.asList("hello world"); + List> result = App.map(sentences); + + // Assert + assertEquals(2, result.size(), "Should have 2 entries"); + assertEquals("hello", result.get(0).getKey()); + assertEquals(1, result.get(0).getValue()); + assertEquals("world", result.get(1).getKey()); + assertEquals(1, result.get(1).getValue()); + } + + @Test + void testMap_multipleSentences() { + + List sentences = Arrays.asList("hello world", "hello java"); + List> result = App.map(sentences); + + // Assert + assertEquals(4, result.size(), "Should have 4 entries (2 words per sentence)"); + assertEquals("hello", result.get(0).getKey()); + assertEquals(1, result.get(0).getValue()); + assertEquals("world", result.get(1).getKey()); + assertEquals(1, result.get(1).getValue()); + assertEquals("hello", result.get(2).getKey()); + assertEquals(1, result.get(2).getValue()); + assertEquals("java", result.get(3).getKey()); + assertEquals(1, result.get(3).getValue()); + } + + @Test + void testReduce_singleWordMultipleEntries() { + + List> mappedWords = Arrays.asList( + new AbstractMap.SimpleEntry<>("hello", 1), + new AbstractMap.SimpleEntry<>("hello", 1) + ); + Map result = App.reduce(mappedWords); + + // Assert + assertEquals(1, result.size(), "Should only contain one unique word"); + assertEquals(2, result.get("hello"), "The count of 'hello' should be 2"); + } + + @Test + void testReduce_multipleWords() { + List> mappedWords = Arrays.asList( + new AbstractMap.SimpleEntry<>("hello", 1), + new AbstractMap.SimpleEntry<>("world", 1), + new AbstractMap.SimpleEntry<>("hello", 1), + new AbstractMap.SimpleEntry<>("java", 1) + ); + Map result = App.reduce(mappedWords); + + // Assert + assertEquals(3, result.size(), "Should contain 3 unique words"); + assertEquals(2, result.get("hello"), "The count of 'hello' should be 2"); + assertEquals(1, result.get("world"), "The count of 'world' should be 1"); + assertEquals(1, result.get("java"), "The count of 'java' should be 1"); + } + +} + diff --git a/pom.xml b/pom.xml index 39a36ce197e3..c4506f5070eb 100644 --- a/pom.xml +++ b/pom.xml @@ -217,6 +217,7 @@ virtual-proxy function-composition microservices-distributed-tracing + map-reduce From 8f71ed9143404459258c3920afbe6bdb8f588084 Mon Sep 17 00:00:00 2001 From: Wizzac <13040359+wizzac@users.noreply.github.com> Date: Wed, 9 Oct 2024 23:12:09 -0300 Subject: [PATCH 2/2] add uml --- map-reduce/etc/map-reduce.png | Bin 0 -> 19000 bytes map-reduce/etc/model-view-intent.urm.puml | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 map-reduce/etc/map-reduce.png create mode 100644 map-reduce/etc/model-view-intent.urm.puml diff --git a/map-reduce/etc/map-reduce.png b/map-reduce/etc/map-reduce.png new file mode 100644 index 0000000000000000000000000000000000000000..fee76e22b3115311699a2e9cd5cac8ea93aa7918 GIT binary patch literal 19000 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 literal 0 HcmV?d00001 diff --git a/map-reduce/etc/model-view-intent.urm.puml b/map-reduce/etc/model-view-intent.urm.puml new file mode 100644 index 000000000000..f9f3533121b7 --- /dev/null +++ b/map-reduce/etc/model-view-intent.urm.puml @@ -0,0 +1,10 @@ +@startuml +package com.iluwatar.mapreduce { + + class App { + + App() + + main(args : String[]) {static} + + map(sentences : List) : List> {static} + + reduce(mappedWords : List>) : Map {static} +} +@enduml \ No newline at end of file