├── .gitignore ├── img └── solution.png ├── pom.xml ├── readme.md └── src ├── main └── java │ └── com │ └── github │ └── hubertwo │ └── kata │ └── stream │ └── Fruit.java └── test ├── java └── com │ └── github │ └── hubertwo │ └── kata │ └── stream │ └── basics │ ├── BasicsJava11Test.java │ ├── BasicsTest.java │ ├── FibonacciSequenceTest.java │ └── PalindromeTest.java └── resources └── java11 └── fruitList.txt /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | *.iml 4 | 5 | -------------------------------------------------------------------------------- /img/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HubertWo/java-stream-kata/c020be62b6dc25eecf91abeee31c91bc1fcf8f0f/img/solution.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.hubertwo.kata.stream 8 | java-stream-kata 9 | 1.0-SNAPSHOT 10 | 11 | 12 | UTF-8 13 | 14 | 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-compiler-plugin 20 | 3.8.1 21 | 22 | 11 23 | 11 24 | 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-surefire-plugin 29 | 2.22.1 30 | 31 | 32 | 33 | 34 | 35 | org.junit.jupiter 36 | junit-jupiter-api 37 | 5.3.2 38 | test 39 | 40 | 41 | org.junit.jupiter 42 | junit-jupiter-engine 43 | 5.3.2 44 | test 45 | 46 | 47 | org.assertj 48 | assertj-core 49 | 3.12.2 50 | test 51 | 52 | 53 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Java Stream Kata 2 | 3 | > A code kata is an exercise in programming which helps programmers hone their skills through practice and repetition. 4 | > https://en.wikipedia.org/wiki/Kata_(programming) 5 | 6 | This repository contains different tasks related to Java Streams. 7 | 8 | ## Leave a :star: :) 9 | If you like the content do not forget to leave a star at the top right corner. Thank you! 10 | 11 | https://github.com/HubertWo/java-stream-kata 12 | 13 | ## Setup 14 | Minimal setup is required. Tools you will need: 15 | - Java 11+ 16 | - Maven 17 | - IDE (In my case IntelliJ IDEA) 18 | 19 | ## How to run exercises 20 | Each exercise is an jUnit test. 21 | You can run test from both Maven or any modern IDE. 22 | 23 | ## Where are answers? 24 | Each task has solution in "Click here to see the answer" block. 25 | 26 | 27 | #### Branch with all answers 28 | Branch ```answers``` has all answers in place and all tests are green. 29 | 30 | ### Maven 31 | To check *task1* from *Basics* package: 32 | ``` 33 | mvn surefire:test -Dtest=BasicsTest#task1 34 | ``` 35 | 36 | ### IDE 37 | After importing project press "Play" near the test you want to run. 38 | 39 | ## More 40 | - Java Stream API: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/package-summary.html 41 | - How to run single test using Maven : https://maven.apache.org/surefire/maven-surefire-plugin/examples/single-test.html 42 | - How to run single test using IntelliJ IDEA: https://www.jetbrains.com/help/idea/performing-tests.html 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/hubertwo/kata/stream/Fruit.java: -------------------------------------------------------------------------------- 1 | package com.github.hubertwo.kata.stream; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Fruit representation. 7 | * 8 | * @author https://github.com/HubertWo 9 | */ 10 | public final class Fruit { 11 | private final String name; 12 | private final int calories; 13 | 14 | public Fruit(String name, int calories) { 15 | this.name = name; 16 | this.calories = calories; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public int getCalories() { 24 | return calories; 25 | } 26 | 27 | /** 28 | * Returning only name of fruit for readability. 29 | */ 30 | @Override 31 | public String toString() { 32 | return getName(); 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | if (this == o) return true; 38 | if (o == null || getClass() != o.getClass()) return false; 39 | Fruit fruit = (Fruit) o; 40 | return calories == fruit.calories && 41 | Objects.equals(name, fruit.name); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return Objects.hash(name, calories); 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/java/com/github/hubertwo/kata/stream/basics/BasicsJava11Test.java: -------------------------------------------------------------------------------- 1 | package com.github.hubertwo.kata.stream.basics; 2 | 3 | import com.github.hubertwo.kata.stream.Fruit; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.io.IOException; 8 | import java.net.URI; 9 | import java.net.URISyntaxException; 10 | import java.nio.file.Files; 11 | import java.nio.file.Paths; 12 | import java.util.List; 13 | import java.util.Set; 14 | import java.util.function.Predicate; 15 | 16 | import static java.util.stream.Collectors.toUnmodifiableList; 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | /** 20 | * By doing this tasks you will learn how to use: 21 | *

22 | * - {@link Predicate#not} 23 | * - {@link String#lines} 24 | *

25 | * 26 | * @author https://github.com/HubertWo 27 | */ 28 | @SuppressWarnings("SimplifyStreamApiCallChains") 29 | @DisplayName("Stream basics - Java 11") 30 | class BasicsJava11Test { 31 | 32 | private static final String LINE_SEPARATOR = System.getProperty("line.separator"); 33 | 34 | private static final Fruit BANANA = new Fruit("Banana", 105); 35 | private static final Fruit PAPAYA = new Fruit("Papaya", 109); 36 | private static final Fruit KIWI = new Fruit("Kiwi", 46); 37 | private static final Fruit MANGO = new Fruit("Mango", 107); 38 | private static final Fruit PEACH = new Fruit("Peach", 48); 39 | 40 | private static final Set FRUITS = Set.of(PAPAYA, BANANA, KIWI, MANGO, PEACH); 41 | 42 | // 43 | /* 44 | FRUITS.stream() 45 | .filter(Predicate.not(f -> f.equals(BANANA))) 46 | .collect(toUnmodifiableList()); 47 | */ 48 | // 49 | @Test 50 | @DisplayName("Task: Get all fruits except BANANA") 51 | void task1() { 52 | final List everythingExceptBanana = FRUITS.stream() 53 | // TODO: put your answer here 54 | .collect(toUnmodifiableList()); 55 | 56 | assertThat(everythingExceptBanana) 57 | .doesNotContain(BANANA) 58 | .containsExactlyInAnyOrder(PAPAYA, KIWI, MANGO, PEACH); 59 | } 60 | 61 | // 62 | /* 63 | givenFruitList 64 | .lines() 65 | .map(line -> line.split(",")) 66 | .map(splittedLine -> new Fruit(splittedLine[0], Integer.parseInt(splittedLine[1]))) 67 | .collect(toList()); 68 | */ 69 | // 70 | @Test 71 | @DisplayName("Task: Load fruit list from file") 72 | void task2() throws IOException, URISyntaxException { 73 | // Loads the String from resources file 74 | URI resource = ClassLoader.getSystemResource("java11/fruitList.txt").toURI(); 75 | final String givenFruitList = Files.readString(Paths.get(resource)); 76 | 77 | // TODO: convert givenFruitList to list using Streams 78 | final List actualFruitList = null; 79 | 80 | assertThat(actualFruitList) 81 | .containsExactlyInAnyOrderElementsOf(FRUITS); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/com/github/hubertwo/kata/stream/basics/BasicsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.hubertwo.kata.stream.basics; 2 | 3 | import com.github.hubertwo.kata.stream.Fruit; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.*; 8 | import java.util.function.Function; 9 | import java.util.function.Supplier; 10 | import java.util.function.ToIntFunction; 11 | import java.util.stream.Collectors; 12 | import java.util.stream.Stream; 13 | 14 | import static java.util.stream.Collectors.toList; 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | /** 18 | * By doing this tasks you will learn how to use: 19 | *

20 | * - {@link Stream#sorted} 21 | * - {@link Stream#limit(long)} 22 | * - {@link Stream#reduce} 23 | * - {@link Stream#mapToInt(ToIntFunction)} 24 | * - {@link Stream#flatMap(Function)} 25 | * - {@link Collectors#groupingBy(Function)} 26 | * - {@link Collectors#counting()} 27 | * - {@link Comparator#comparing(Function)} 28 | * - {@link Function#identity()} 29 | * - {@link Stream#generate(Supplier)} 30 | *

31 | * 32 | * @author https://github.com/HubertWo 33 | */ 34 | @SuppressWarnings("SimplifyStreamApiCallChains") 35 | @DisplayName("Stream basics") 36 | class BasicsTest { 37 | 38 | private static final Fruit BANANA = new Fruit("Banana", 105); 39 | private static final Fruit PAPAYA = new Fruit("Papaya", 109); 40 | private static final Fruit KIWI = new Fruit("Kiwi", 46); 41 | private static final Fruit MANGO = new Fruit("Mango", 107); 42 | private static final Fruit PEACH = new Fruit("Peach", 48); 43 | 44 | private static final Set FRUITS = Set.of(PAPAYA, BANANA, KIWI, MANGO, PEACH); 45 | 46 | // 47 | /* 48 | .sorted(Comparator.comparing(Fruit::getCalories).reversed()) 49 | .limit(2) 50 | .collect(toList()); 51 | */ 52 | // 53 | @Test 54 | @DisplayName("Task: Find 2 fruits with biggest amount of calories") 55 | void task1() { 56 | List mostCaloricFruits = FRUITS.stream().collect(toList()); // TODO: FRUITS.stream() 57 | 58 | assertThat(mostCaloricFruits).containsExactly(PAPAYA, MANGO); 59 | } 60 | 61 | // 62 | /* 63 | FRUITS.stream().mapToInt((Fruit fruit) -> fruit.getCalories() / 2).sum(); 64 | */ 65 | // 66 | @Test 67 | @DisplayName("Task: Take half of each fruit and get the sum of calories") 68 | void task2() { 69 | final int sumOfCalories = 0; // TODO: FRUITS.stream() 70 | 71 | assertThat(sumOfCalories).isEqualTo(206); 72 | } 73 | 74 | // 75 | /* 76 | FRUITS.stream() 77 | .collect(Collectors 78 | .groupingBy((Fruit fruit) -> fruit.getName().charAt(0), Collectors.toSet() 79 | ) 80 | )); 81 | */ 82 | // 83 | @Test 84 | @DisplayName("Task: Group fruits by first letter") 85 | void task3() { 86 | final Map> mapOfFruits = Collections.emptyMap(); // TODO: FRUITS.stream() 87 | 88 | assertThat(mapOfFruits.keySet()).contains('B', 'K', 'M', 'P'); 89 | assertThat(mapOfFruits.get('B')).hasSize(1); 90 | assertThat(mapOfFruits.get('K')).hasSize(1); 91 | assertThat(mapOfFruits.get('M')).hasSize(1); 92 | assertThat(mapOfFruits.get('P')).hasSize(2); 93 | } 94 | 95 | // 96 | /* 97 | Solution 1. 98 | 99 | fruitBaskets.stream() 100 | .flatMap(Collection::stream) 101 | .collect(Collectors.toList()); 102 | 103 | Solution 2 - just to illustrate the concept of flatMap. 104 | 105 | fruitBaskets.stream() 106 | .map(basket -> basket.stream()) 107 | .reduce(Stream.empty(), Stream::concat) 108 | .collect(Collectors.toList()); 109 | 110 | */ 111 | // 112 | @Test 113 | @DisplayName("Task: Put all fruits into one basket") 114 | void task4() { 115 | final List> fruitBaskets = List.of( 116 | List.of(BANANA, PAPAYA), 117 | List.of(MANGO, PEACH) 118 | ); 119 | 120 | final List basketWithAllFruits = Collections.emptyList(); // TODO: fruitBaskets.stream() 121 | 122 | assertThat(basketWithAllFruits).contains(BANANA, PAPAYA, MANGO, PEACH); 123 | assertThat(basketWithAllFruits).doesNotContain(KIWI); 124 | } 125 | 126 | // 127 | /* 128 | basket.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); 129 | */ 130 | // 131 | @Test 132 | @DisplayName("Task: Count amount of each fruit in the basket") 133 | void task5() { 134 | final List basket = List.of(MANGO, PAPAYA, MANGO, PEACH, KIWI, KIWI, KIWI); 135 | 136 | final Map countedFruit = Collections.emptyMap(); // TODO: basket.stream() 137 | 138 | assertThat(countedFruit.keySet()).contains(MANGO, PAPAYA, PEACH, KIWI); 139 | 140 | assertThat(countedFruit.get(MANGO)).isEqualTo(2); 141 | assertThat(countedFruit.get(PAPAYA)).isEqualTo(1); 142 | assertThat(countedFruit.get(PEACH)).isEqualTo(1); 143 | assertThat(countedFruit.get(KIWI)).isEqualTo(3); 144 | } 145 | 146 | // 147 | /* 148 | FRUITS.stream() 149 | .reduce(new Fruit("", 0), (mixedFruit, fruitToAdd) -> new Fruit( 150 | mixedFruit.getName() + fruitToAdd.getName(), 151 | mixedFruit.getCalories() + fruitToAdd.getCalories() 152 | )); 153 | */ 154 | // 155 | @Test 156 | @DisplayName("Task: Mix all fruits together and construct one, big, new Fruit") 157 | void task6() { 158 | final Fruit bigJuicyFruit = null; // TODO: FRUITS.stream() 159 | 160 | assertThat(bigJuicyFruit).isNotNull(); 161 | assertThat(bigJuicyFruit.getCalories()).isEqualTo(415); 162 | assertThat(bigJuicyFruit.getName()).isNotEmpty(); 163 | } 164 | 165 | // 166 | /* 167 | final Stream infiniteStreamOfFruits = Stream 168 | .generate(randomFruitSupplier); 169 | */ 170 | // 171 | @Test 172 | @DisplayName("Task: Generate list of 10 randomly picked fruits") 173 | void task7() { 174 | 175 | // Random fruit supplier 176 | final Supplier randomFruitSupplier = new Supplier<>() { 177 | final Random random = new Random(); 178 | final List fruits = new ArrayList<>(FRUITS); 179 | 180 | @Override 181 | public Fruit get() { 182 | return fruits.get(random.nextInt(fruits.size())); 183 | } 184 | }; 185 | 186 | final Stream infiniteStreamOfFruits = Stream.empty(); // TODO: Stream. 187 | 188 | 189 | List pickedFruits = infiniteStreamOfFruits 190 | .limit(10) 191 | .collect(Collectors.toList()); 192 | 193 | assertThat(pickedFruits).hasSize(10); 194 | } 195 | 196 | // 197 | /* 198 | IntStream 199 | .range(0, fruitList.size()) 200 | .filter(i -> i % 2 == 1) 201 | .mapToObj(fruitList::get) 202 | .collect(toList()); 203 | */ 204 | // 205 | @Test 206 | @DisplayName("Task: Collect only second and forth (with odd index) fruit from fruitList") 207 | void task8() { 208 | // Do you know why list have to be sorted? 209 | final List fruitList = new ArrayList<>(FRUITS).stream() 210 | .sorted(Comparator.comparing(Fruit::getName)) 211 | .collect(toList()); 212 | 213 | List filteredFruits = fruitList; // TODO: change to stream. 214 | 215 | assertThat(filteredFruits).hasSize(2); 216 | assertThat(filteredFruits).containsExactly(KIWI, PAPAYA); 217 | } 218 | 219 | } 220 | -------------------------------------------------------------------------------- /src/test/java/com/github/hubertwo/kata/stream/basics/FibonacciSequenceTest.java: -------------------------------------------------------------------------------- 1 | package com.github.hubertwo.kata.stream.basics; 2 | 3 | import org.assertj.core.data.Percentage; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | 10 | /** 11 | * This task you will show you how to use: 12 | *

13 | * - {@link java.util.stream.Stream#iterate)} 14 | * - {@link java.util.stream.Stream#reduce)} 15 | *

16 | * 17 | * @author https://github.com/HubertWo 18 | */ 19 | @DisplayName("Fibonacci Sequence") 20 | class FibonacciSequenceTest { 21 | 22 | // 23 | /* 24 | return Stream 25 | .iterate(new long[]{1, 1}, (long[] results) -> new long[]{results[1], results[0] + results[1]}) 26 | .limit(sequenceIndex) 27 | .reduce((a, b) -> b) 28 | .orElse(new long[]{0, 0})[0]; 29 | */ 30 | // 31 | 32 | /** 33 | * Calculates {@param sequenceIndex} of Fibonacci sequence. 34 | * 35 | * @see Wikipedia 36 | */ 37 | static long fibonacciSequence(long sequenceIndex) { 38 | throw new IllegalStateException("Not implemented yet"); 39 | } 40 | 41 | @Test 42 | @DisplayName("Task: Calculate Fibonacci Sequence") 43 | void task() { 44 | assertThat(fibonacciSequence(0)).isEqualTo(0); 45 | assertThat(fibonacciSequence(1)).isEqualTo(1); 46 | assertThat(fibonacciSequence(2)).isEqualTo(1); 47 | assertThat(fibonacciSequence(3)).isEqualTo(2); 48 | assertThat(fibonacciSequence(10)).isEqualTo(55); 49 | 50 | final double goldenRatio = (double) fibonacciSequence(20) / fibonacciSequence(19); 51 | assertThat(goldenRatio).isCloseTo(1.61d, Percentage.withPercentage(2)); 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/github/hubertwo/kata/stream/basics/PalindromeTest.java: -------------------------------------------------------------------------------- 1 | package com.github.hubertwo.kata.stream.basics; 2 | 3 | import org.junit.jupiter.api.DisplayName; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.function.Function; 7 | import java.util.stream.Collectors; 8 | import java.util.stream.Stream; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | /** 13 | * This task you will show you how to use: 14 | *

15 | * - {@link Collectors#groupingBy(Function)}} 16 | * - {@link Function#identity()} 17 | * - {@link Collectors#counting()} 18 | * - {@link Stream#count()} 19 | *

20 | * 21 | * @author https://github.com/HubertWo 22 | */ 23 | @DisplayName("Palindrome") 24 | class PalindromeTest { 25 | 26 | /** 27 | * Word is an palindrome candidate when each character of word occurs even number of 28 | * times or only one character occurs odd number of times. 29 | * 30 | * @return true if {@param word} is an palindrome candidate 31 | */ 32 | // 33 | /* 34 | if (word.length() == 0) { 35 | return false; 36 | } 37 | 38 | final Map countedCharacters = word 39 | .chars() 40 | .boxed() 41 | .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); 42 | 43 | // Count number of characters which occurred odd number of times 44 | final long oddCharactersCount = countedCharacters.values().stream() 45 | .filter(count -> count % 2 != 0) 46 | .count(); 47 | 48 | // Only one character with odd number of occurrences is allowed 49 | return oddCharactersCount <= 1; 50 | */ 51 | // 52 | private static boolean isPalindromeCandidate(final String word) { 53 | // TODO: implement using {@link Stream} 54 | throw new IllegalStateException("Not implemented yet"); 55 | } 56 | 57 | @Test 58 | @DisplayName("Is palindrome candidate") 59 | void palindromeCandidate() { 60 | assertThat(isPalindromeCandidate("")).isFalse(); 61 | assertThat(isPalindromeCandidate("a")).isTrue(); 62 | assertThat(isPalindromeCandidate("ab")).isFalse(); 63 | assertThat(isPalindromeCandidate("aab")).isTrue(); 64 | assertThat(isPalindromeCandidate("aabb")).isTrue(); 65 | assertThat(isPalindromeCandidate("aabbc")).isTrue(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/resources/java11/fruitList.txt: -------------------------------------------------------------------------------- 1 | Mango,107 2 | Kiwi,46 3 | Papaya,109 4 | Peach,48 5 | Banana,105 --------------------------------------------------------------------------------