Статьи

Java 8 Streams: Микро Катас

Программирование ката — это упражнение, которое помогает программисту отточить свои навыки посредством практики и повторения.

Эта статья является частью серии Java Tutorial Through Katas .

В статье предполагается, что читатель уже имеет опыт работы с Java, что он знаком с основами модульных тестов и знает, как запускать их из своей любимой среды IDE (моя — IntelliJ IDEA ).

Идея, стоящая за упражнениями, показанными ниже, заключается в изучении потоковой передачи Java 8 с использованием подхода разработки, основанного на тестировании (напишите реализацию для первого теста, подтвердите, что он прошел, и перейдите к следующему).

Каждый раздел будет начинаться с цели в виде тестов, которые доказывают, что реализация будет правильной после ее написания. Каждый из этих тестов сопровождается одной возможной реализацией в Java 7 (или более ранней версии) и Java 8 с использованием Streams. Таким образом, читатель может сравнить некоторые из новых функций Java 8 с их эквивалентами в более ранних версиях JDK. Пожалуйста, попробуйте выполнить тесты, не глядя на предоставленные решения.

Для получения дополнительной информации о передовых методах TDD, пожалуйста, прочитайте Test Driven Development (TDD): Лучшие практики с использованием примеров Java .

Карта Java 8

Преобразуйте элементы коллекции в верхний регистр.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
 
import static com.technologyconversations.java8exercises.streams.ToUpperCase.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Convert elements of a collection to upper case.
 */
public class ToUpperCaseSpec {
 
    @Test
    public void transformShouldConvertCollectionElementsToUpperCase() {
        List<String> collection = asList("My"< "name"< "is"< "John"< "Doe");
        List<String> expected = asList("MY"< "NAME"< "IS"< "JOHN"< "DOE");
        assertThat(transform(collection)).hasSameElementsAs(expected);
    }
 
}

Реализации Java 7 (transform7) и Java8 (transform)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.technologyconversations.java8exercises.streams;
 
import java.util.ArrayList;
import java.util.List;
 
import static java.util.stream.Collectors.toList;
 
public class ToUpperCase {
 
    public static List<String> transform7(List<String> collection) {
        List<String> coll = new ArrayList<>();
        for (String element : collection) {
            coll.add(element.toUpperCase());
        }
        return coll;
    }
 
    public static List<String> transform(List<String> collection) {
        return collection.stream() // Convert collection to Stream
                .map(String::toUpperCase) // Convert each element to upper case
                .collect(toList()); // Collect results to a new list
    }
 
}

Фильтр Java 8

Отфильтруйте коллекцию так, чтобы возвращались только элементы длиной менее 4 символов.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
 
import static com.technologyconversations.java8exercises.streams.FilterCollection.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Filter collection so that only elements with less then 4 characters are returned.
 */
public class FilterCollectionSpec {
 
    @Test
    public void transformShouldFilterCollection() {
        List<String> collection = asList("My", "name", "is", "John", "Doe");
        List<String> expected = asList("My", "is", "Doe");
        assertThat(transform(collection)).hasSameElementsAs(expected);
    }
 
}

Реализации Java 7 (transform7) и Java8 (transform)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.technologyconversations.java8exercises.streams;
 
import java.util.ArrayList;
import java.util.List;
 
import static java.util.stream.Collectors.toList;
 
public class FilterCollection {
 
    public static List<String> transform7(List<String> collection) {
        List<String> newCollection = new ArrayList<>();
        for (String element : collection) {
            if (element.length() < 4) {
                newCollection.add(element);
            }
        }
        return newCollection;
    }
 
    public static List<String> transform(List<String> collection) {
        return collection.stream() // Convert collection to Stream
                .filter(value -> value.length() < 4) // Filter elements with length smaller than 4 characters
                .collect(toList()); // Collect results to a new list
    }
 
}

Java 8 flatMap

Свести многомерную коллекцию.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
 
import static com.technologyconversations.java8exercises.streams.FlatCollection.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Flatten multidimensional collection
 */
public class FlatCollectionSpec {
 
    @Test
    public void transformShouldFlattenCollection() {
        List<List<String>> collection = asList(asList("Viktor", "Farcic"), asList("John", "Doe", "Third"));
        List<String> expected = asList("Viktor", "Farcic", "John", "Doe", "Third");
        assertThat(transform(collection)).hasSameElementsAs(expected);
    }
 
}

Реализации Java 7 (transform7) и Java8 (transform)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.technologyconversations.java8exercises.streams;
 
import java.util.ArrayList;
import java.util.List;
 
import static java.util.stream.Collectors.toList;
 
public class FlatCollection {
 
    public static List<String> transform7(List<List<String>> collection) {
        List<String> newCollection = new ArrayList<>();
        for (List<String> subCollection : collection) {
            for (String value : subCollection) {
                newCollection.add(value);
            }
        }
        return newCollection;
    }
 
    public static List<String> transform(List<List<String>> collection) {
        return collection.stream() // Convert collection to Stream
                .flatMap(value -> value.stream()) // Replace list with stream
                .collect(toList()); // Collect results to a new list
    }
 
}

Java 8 Макс и компаратор

Получить старейшего человека из коллекции.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
 
import static com.technologyconversations.java8exercises.streams.OldestPerson.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Get oldest person from the collection
 */
public class OldestPersonSpec {
 
    @Test
    public void getOldestPersonShouldReturnOldestPerson() {
        Person sara = new Person("Sara", 4);
        Person viktor = new Person("Viktor", 40);
        Person eva = new Person("Eva", 42);
        List<Person> collection = asList(sara, eva, viktor);
        assertThat(getOldestPerson(collection)).isEqualToComparingFieldByField(eva);
    }
 
}

Реализации Java 7 (getOldestPerson7) и Java8 (getOldestPerson)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.technologyconversations.java8exercises.streams;
 
import java.util.Comparator;
import java.util.List;
 
public class OldestPerson {
 
    public static Person getOldestPerson7(List<Person> people) {
        Person oldestPerson = new Person("", 0);
        for (Person person : people) {
            if (person.getAge() > oldestPerson.getAge()) {
                oldestPerson = person;
            }
        }
        return oldestPerson;
    }
 
    public static Person getOldestPerson(List<Person> people) {
        return people.stream() // Convert collection to Stream
                .max(Comparator.comparing(Person::getAge)) // Compares people ages
                .get(); // Gets stream result
    }
 
}

Java 8 сумма и уменьшение

Суммируйте все элементы коллекции.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
 
import static com.technologyconversations.java8exercises.streams.Sum.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Sum all elements of a collection
 */
public class SumSpec {
 
    @Test
    public void transformShouldConvertCollectionElementsToUpperCase() {
        List<Integer> numbers = asList(1, 2, 3, 4, 5);
        assertThat(calculate(numbers)).isEqualTo(1 + 2 + 3 + 4 + 5);
    }
 
}

Реализации Java 7 (вычисления7) и Java8 (вычисления)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package com.technologyconversations.java8exercises.streams;
 
import java.util.List;
 
public class Sum {
 
    public static int calculate7(List<Integer> numbers) {
        int total = 0;
        for (int number : numbers) {
            total += number;
        }
        return total;
    }
 
    public static int calculate(List<Integer> people) {
        return people.stream() // Convert collection to Stream
                .reduce(0, (total, number) -> total + number); // Sum elements with 0 as starting value
    }
 
}

Фильтр и карта Java 8

Получить имена всех детей (в возрасте до 18 лет).

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
 
import static com.technologyconversations.java8exercises.streams.Kids.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Get names of all kids (under age of 18)
 */
public class KidsSpec {
 
    @Test
    public void getKidNameShouldReturnNamesOfAllKidsFromNorway() {
        Person sara = new Person("Sara", 4);
        Person viktor = new Person("Viktor", 40);
        Person eva = new Person("Eva", 42);
        Person anna = new Person("Anna", 5);
        List<Person> collection = asList(sara, eva, viktor, anna);
        assertThat(getKidNames(collection))
                .contains("Sara", "Anna")
                .doesNotContain("Viktor", "Eva");
    }
 
}

Java 7 (getKidNames7) и Java8 (getKidNames) реализации

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.technologyconversations.java8exercises.streams;
 
import java.util.*;
 
import static java.util.stream.Collectors.toSet;
 
public class Kids {
 
    public static Set<String> getKidNames7(List<Person> people) {
        Set<String> kids = new HashSet<>();
        for (Person person : people) {
            if (person.getAge() < 18) {
                kids.add(person.getName());
            }
        }
        return kids;
    }
 
    public static Set<String> getKidNames(List<Person> people) {
        return people.stream()
                .filter(person -> person.getAge() < 18) // Filter kids (under age of 18)
                .map(Person::getName) // Map Person elements to names
                .collect(toSet()); // Collect values to a Set
    }
 
}

Java 8 summaryStatistics

Получить статистику людей: средний возраст, количество, максимальный возраст, минимальный возраст и сумма всех возрастов.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
 
import static com.technologyconversations.java8exercises.streams.PeopleStats.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Get people statistics: average age, count, maximum age, minimum age and sum og all ages.
 */
public class PeopleStatsSpec {
 
    Person sara = new Person("Sara", 4);
    Person viktor = new Person("Viktor", 40);
    Person eva = new Person("Eva", 42);
    List<Person> collection = asList(sara, eva, viktor);
 
    @Test
    public void getStatsShouldReturnAverageAge() {
        assertThat(getStats(collection).getAverage())
                .isEqualTo((double)(4 + 40 + 42) / 3);
    }
 
    @Test
    public void getStatsShouldReturnNumberOfPeople() {
        assertThat(getStats(collection).getCount())
                .isEqualTo(3);
    }
 
    @Test
    public void getStatsShouldReturnMaximumAge() {
        assertThat(getStats(collection).getMax())
                .isEqualTo(42);
    }
 
    @Test
    public void getStatsShouldReturnMinimumAge() {
        assertThat(getStats(collection).getMin())
                .isEqualTo(4);
    }
 
    @Test
    public void getStatsShouldReturnSumOfAllAges() {
        assertThat(getStats(collection).getSum())
                .isEqualTo(40 + 42 + 4);
    }
 
}

Реализации Java 7 (getStats7) и Java8 (getStats)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.technologyconversations.java8exercises.streams;
 
import java.util.IntSummaryStatistics;
import java.util.List;
 
public class PeopleStats {
 
    public static Stats getStats7(List<Person> people) {
        long sum = 0;
        int min = people.get(0).getAge();
        int max = 0;
        for (Person person : people) {
            int age = person.getAge();
            sum += age;
            min = Math.min(min, age);
            max = Math.max(max, age);
        }
        return new Stats(people.size(), sum, min, max);
    }
 
    public static IntSummaryStatistics getStats(List<Person> people) {
        return people.stream()
                .mapToInt(Person::getAge)
                .summaryStatistics();
    }
 
}

Java 8 partitioningBy

Разделение взрослых и детей.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
import java.util.Map;
 
import static com.technologyconversations.java8exercises.streams.Partitioning.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Partition adults and kids
 */
public class PartitioningSpec {
 
    @Test
    public void partitionAdultsShouldSeparateKidsFromAdults() {
        Person sara = new Person("Sara", 4);
        Person viktor = new Person("Viktor", 40);
        Person eva = new Person("Eva", 42);
        List<Person> collection = asList(sara, eva, viktor);
        Map<Boolean, List<Person>> result = partitionAdults(collection);
        assertThat(result.get(true)).hasSameElementsAs(asList(viktor, eva));
        assertThat(result.get(false)).hasSameElementsAs(asList(sara));
    }
 
}

Java 7 (partitionAdults7) и Java8 (partitionAdults) реализации

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.technologyconversations.java8exercises.streams;
 
import java.util.*;
import static java.util.stream.Collectors.*;
 
public class Partitioning {
 
    public static Map<Boolean, List<Person>> partitionAdults7(List<Person> people) {
        Map<Boolean, List<Person>> map = new HashMap<>();
        map.put(true, new ArrayList<>());
        map.put(false, new ArrayList<>());
        for (Person person : people) {
            map.get(person.getAge() >= 18).add(person);
        }
        return map;
    }
 
    public static Map<Boolean, List<Person>> partitionAdults(List<Person> people) {
        return people.stream() // Convert collection to Stream
                .collect(partitioningBy(p -> p.getAge() >= 18)); // Partition stream of people into adults (age => 18) and kids
    }
 
}

Java 8 groupingBy

Группировка людей по национальности.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
import java.util.Map;
 
import static com.technologyconversations.java8exercises.streams.Grouping.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Group people by nationality
 */
public class GroupingSpec {
 
    @Test
    public void partitionAdultsShouldSeparateKidsFromAdults() {
        Person sara = new Person("Sara", 4, "Norwegian");
        Person viktor = new Person("Viktor", 40, "Serbian");
        Person eva = new Person("Eva", 42, "Norwegian");
        List<Person> collection = asList(sara, eva, viktor);
        Map<String, List<Person>> result = groupByNationality(collection);
        assertThat(result.get("Norwegian")).hasSameElementsAs(asList(sara, eva));
        assertThat(result.get("Serbian")).hasSameElementsAs(asList(viktor));
    }
 
}

Java 7 (groupByNationality7) и Java8 (groupByNationality) реализации

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.technologyconversations.java8exercises.streams;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import static java.util.stream.Collectors.*;
 
public class Grouping {
 
    public static Map<String, List<Person>> groupByNationality7(List<Person> people) {
        Map<String, List<Person>> map = new HashMap<>();
        for (Person person : people) {
            if (!map.containsKey(person.getNationality())) {
                map.put(person.getNationality(), new ArrayList<>());
            }
            map.get(person.getNationality()).add(person);
        }
        return map;
    }
 
    public static Map<String, List<Person>> groupByNationality(List<Person> people) {
        return people.stream() // Convert collection to Stream
                .collect(groupingBy(Person::getNationality)); // Group people by nationality
    }
 
}

Java 8 присоединяется

Вернуть имена людей через запятую.

тесты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.technologyconversations.java8exercises.streams;
 
import org.junit.Test;
 
import java.util.List;
 
import static com.technologyconversations.java8exercises.streams.Joining.namesToString;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
 
/*
Return people names separated by comma
 */
public class JoiningSpec {
 
    @Test
    public void toStringShouldReturnPeopleNamesSeparatedByComma() {
        Person sara = new Person("Sara", 4);
        Person viktor = new Person("Viktor", 40);
        Person eva = new Person("Eva", 42);
        List<Person> collection = asList(sara, viktor, eva);
        assertThat(namesToString(collection))
                .isEqualTo("Names: Sara, Viktor, Eva.");
    }
 
}

Реализации Java 7 (namesToString7) и Java8 (namesToString)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.technologyconversations.java8exercises.streams;
 
import java.util.List;
 
import static java.util.stream.Collectors.joining;
 
public class Joining {
 
    public static String namesToString7(List<Person> people) {
        String label = "Names: ";
        StringBuilder sb = new StringBuilder(label);
        for (Person person : people) {
            if (sb.length() > label.length()) {
                sb.append(", ");
            }
            sb.append(person.getName());
        }
        sb.append(".");
        return sb.toString();
    }
 
    public static String namesToString(List<Person> people) {
        return people.stream() // Convert collection to Stream
                .map(Person::getName) // Map Person to name
                .collect(joining(", ", "Names: ", ".")); // Join names
    }
 
}

Источник

Полный исходный код находится в репозитории GitHub https://github.com/vfarcic/java-8-exercises . Помимо тестов и реализации, репозиторий включает build.gradle, который может использоваться, среди прочего, для загрузки зависимостей AssertJ и запуска тестов.

Ссылка: Java 8 Streams: Micro Katas от нашего партнера по JCG Виктора Фарчича в блоге « Технологии»