Введение
Идея, которая лежит в упражнениях, приведенных ниже, это изучить Streaming Java 8, используя подход test-driven development (пишем имплементацию для первого теста, убеждаемся, что она работает и двигаемся к следующему).
Каждая секция начинается с темы в форме тестов, которые докажут, что имплементация, которую мы напишем, будет корректно работать. Каждый из тестов сопровождается имплементацией на Java 7 и на Java 8, используя Stream API. Таким способом мы сравним некоторые особенности Java 8 с эквивалентом из Java 7.
Java 8 map
Приведите элементы коллекции в вид с верхним регистром.
Тест
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); assertThat(transform7(collection)).hasSameElementsAs(expected); } }
Имплементации на Java 7 и Java 8
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 filter
Отфильтруйте коллекцию так что вернуться только элементы с менее 4 символами.
Тест
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); assertThat(transform7(collection)).hasSameElementsAs(expected); } }
Имплементации на Java 7 и Java 8
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
Делаем плоской многоуровневую колекцию
Тест
public class FlatMapSpec { @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(FlatCollection.transform(collection)).hasSameElementsAs(expected); assertThat(FlatCollection.transform7(collection)).hasSameElementsAs(expected); } }
Имплементации на Java 7 и Java 8
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(Collectors.toList()); // Collect results to a new list } }
Java 8 max и comparator
Получить самого старшего человека из коллекции
Тест
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); assertThat(getOldestPerson7(collection)).isEqualToComparingFieldByField(eva); } }
Имплементации на Java 7 и Java 8
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 sum и reduce
Получить сумму всех элементов коллекции
Тест
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 и Java 8
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 filter и map
Получить имена всех детей (до 18 лет)
Тест
public class KidsSpec { @Test public void getKidNameShouldReturnNamesOfAllKidsFromList() { 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"); assertThat(getKidNames7(collection)) .contains("Sara", "Anna") .doesNotContain("Viktor", "Eva"); } }
Имплементации на Java 7 и Java 8
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(p -> p.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 summaryStatisctics
Получить стастику по людям: средний возраст, количество, максимальный возраст, минимальный возраст и сумма всех возрастов.
Тест
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); assertThat(getStats7(collection).getAverage()) .isEqualTo((double)(4 + 40 + 42) / 3); } @Test public void getStatsShouldReturnNumberOfPeople() { assertThat(getStats(collection).getCount()) .isEqualTo(3); assertThat(getStats7(collection).getCount()) .isEqualTo(3); } @Test public void getStatsShouldReturnMaximumAge() { assertThat(getStats(collection).getMax()) .isEqualTo(42); assertThat(getStats7(collection).getMax()) .isEqualTo(42); } @Test public void getStatsShouldReturnMinimumAge() { assertThat(getStats(collection).getMin()) .isEqualTo(4); assertThat(getStats7(collection).getMin()) .isEqualTo(4); } @Test public void getStatsShouldReturnSumOfAllAges() { assertThat(getStats(collection).getSum()) .isEqualTo(40 + 42 + 4); assertThat(getStats7(collection).getSum()) .isEqualTo(40 + 42 + 4); } }
Имплементации на Java 7 и Java 8
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(), max, min, sum); } public static IntSummaryStatistics getStats(List<Person> people) { return people.stream() .mapToInt(Person::getAge) .summaryStatistics(); } }
Java 8 partitioningBy
Необходимо разделить взрослых и детей
Тест
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); Map<Boolean, List<Person>> result2 = partitionAdults7(collection); assertThat(result.get(true)).hasSameElementsAs(asList(viktor, eva)); assertThat(result.get(false)).hasSameElementsAs(asList(sara)); assertThat(result2.get(true)).hasSameElementsAs(asList(viktor, eva)); assertThat(result2.get(false)).hasSameElementsAs(asList(sara)); } }
Имплементации на Java 7 и Java 8
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; } // Partition stream of people into adults (age => 18) and kids public static Map<Boolean, List<Person>> partitionAdults(List<Person> people) { return people.stream() .collect(partitioningBy(p -> p.getAge() >= 18)); } }
Java 8 groupingBy
Нужно сгруппировать людей по национальности
Тест
public class GroupungSpec { @Test public void groupByNationatilityShouldSeparatePeopleByNationality() { 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)); result = groupByNationality7(collection); assertThat(result.get("Norwegian")).hasSameElementsAs(asList(sara, eva)); assertThat(result.get("Serbian")).hasSameElementsAs(asList(viktor)); } }
Имплементации на Java 7 и Java 8
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 joining
Необходимо вернуть имена людей, разделенные запятой
Тест
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(namesToString7(collection)) .isEqualTo("Names: Sara, Viktor, Eva."); assertThat(namesToString(collection)) .isEqualTo("Names: Sara, Viktor, Eva."); } }
Имплементации на Java 7 и Java 8
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. Помимо тестов и имплементаций, репозиторий включает в себя pom.xml, который может быть полезен для скачивания зависимости Assertj и запуска тестов.