Stream API
Специализации:
- java.util.stream.Stream
- java.util.stream.IntStream
- java.util.stream.LongStream
- java.util.stream.DoubleStream
Stream
- Поток однотипных данных для однотипной обработки
- Источник создает ленивый стрим
- Промежуточные операции описывают рецепт обработки, но ничего не делают
- Вся работа производится при вызове терминальной операции
- Может потребить не все элементы
- Может быть бесконечным или завершиться за конечное время
Пример:
class Main {
public static void main(String[] args) {
source.stream()
.map(x -> x.squash())
.filter(x -> x.getColor() != YELLOW)
.forEach(System.out::println);
}
}
Некоторые источники Stream
Stream.empty()
Stream.of(x, y, z)
Stream.ofNullable(x)
Stream.generate(Math::random)
Stream.iterate(val, x -> x + 1)
Stream.iterate(0, x -> x < 100, x -> x + 1)
- Java 9collection.stream()
Arrays.stream(array)
- int/long/double/ObjectRandom.ints()/longs()/doubles()
String.chars()
Pattern.aplitAsStream()
- ….
Промежуточные операции (intermediate)
map() mapToInt/mapToLong/mapToDouble/mapToObj
filter()
flatMap() flatMapToInt/flatMapToLong/flatMapToDouble
mapMulti mapMultiToInt/mapMultiToLong/mapMultiToDouble
- Java 16distinct()
sorted()
limit()
skip()
peek()
takeWhile()
- Java 9dropWhile()
- Java 9boxed asLongStream/asDoubleStream
- примитивы
Терминальные операции (terminal)
count()
findFirst()/findAny -> Optional
anyMatch()/noneMatch/allMatch
forEach()/forEachOrdered
max()/min -> Otional
reduce()
collect()
toArray()
toList()
- Java 16sum average/summaryStatistics
- примитивы
Примеры:
import java.util.stream.Collectors;
class Main {
// не правильная реализация
public List<String> processList(List<String> list) {
List<String> result = new ArrayList<>();
list.stream()
.map(String::trim)
.filter(s -> !s.isEmpty())
.forEach(result::add);
return result;
}
// правильная реализация
public List<String> processList(List<String> list) {
return list.stream()
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
}
}
class Main {
public static void main(String[] args) {
List<List<String>> listOfLists = List.of(
List.of("foo", "bar", "baz"),
List.of("Java", "Kotlin", "Groovy"),
List.of("Hello", "World")
);
System.out.println(
listOfLists.stream()
.flatMap(List::stream)
.anyMatch("Java"::equals)
);
}
}
class Main {
// reduce
static BigInteger factorial(int n) {
return IntStream.rangeClosed(1, n)
.mapToObj(BigInteger::valueOf)
.reduce(BigInteger.ONE, BigInteger::multiply);
}
}
Don’t over reduce!
reduce(0, Integer::sum) -> mapToInt(x -> x).sum()
reduce(Integer::max) -> max()
reduce("", String::concat) -> collect(Collectors.joining())
Collector
- Рецепт терминальной операции (способ комбинирования элементов в единое целое)
- Предопределенные коллекторы в классе Collectors
- Некоторые коллекторы можно комбинировать
- Свой коллектор с нуля (Collector.of)
Collectors
toList()
- ArrayList (Не гарантированно)toSet()
- HashSet (Не гарантированно)toCollection(supplier)
toMap(keyMapper, valueMapper[, merger[, mapSupplier]])
joining([separator[, prefix, suffix]])
groupingBy(classifier[[, mapSupplier], downstream])
partitioningBy(predicate[, downstream])
reducing/counting/mapping/minBy/maxBy
averagingInt/averagingLong/averagingDouble
summingInt/summingLong/summingDouble
Задача. Сгруппировать строки по длине
Пример 1
class Main {
// Задача. Сгруппировать строки по длине
static Map<Integer, List<String>> stringsByLength(List<String> list) {
return list.stream().collect(Collectors.groupingBy(String::length));
}
public static void main(String[] args) {
stringsByLength(Arrays.asList("a", "bb", "c", "dd", "eee"));
// {1=[a, c], 2=[bb, dd], 3=[eee]}
}
}
Пример 2
class Main {
static Map<Integer, String> stringsByLength(List<String> list) {
return list.stream().collect(Collectors.groupingBy(String::length, Collectors.joining("+")));
}
public static void main(String[] args) {
stringsByLength(Arrays.asList("a", "bb", "c", "dd", "eee"));
// {1=a+c, 2=bb+dd, 3=eee}
}
}
Пример 3
class Main {
static Map<Integer, Map<Character, List<String>>> strByLenAndFirstLetter(List<String> list) {
return list.stream()
.collect(Collectors.groupingBy(String::length,
Collectors.groupingBy(s -> a.charAt(0))));
}
public static void main(String[] args) {
strByLenAndFirstLetter(Arrays.asList("a", "b", "aa", "ab", "ba"));
// {1={a=[a], b=[b]}, 2={a=[aa, ab], b=[ba]}
}
}
Пример с объектами 1:
interface User {
String name();
}
interface Department {
String title();
User chief();
Stream<User> users();
}
interface Company {
Stream<Department> departments();
}
class Main {
// Список отделов по начальнику
static Map<User, List<Department>> departmentByChief(Company company) {
return company
.departments()
.collect(Collectors.groupingBy(Department::chief));
}
// Список названий отделов по начальнику
static Map<User, List<String>> departmentNamesByChief(Company company) {
return company
.departments()
.collect(Collectors.groupingBy(Department::chief,
Collectors.mapping(Department::title, toList())));
}
// Найти подчиненных для каждого начальника
// Java 9
static Map<User, Set<User>> supervisors(Company company) {
return company
.departments()
.collect(Collectors.groupingBy(Department::chief,
Collectors.flatMapping(Department::users, toSet())));
}
}
Пример с объектами 2:
interface Book {
String getCategory();
int getPrice();
}
class Main {
/* Найти самую дешёвую книгу в каждой категории */
// !! Не верное решение!!
static Map<String, Integer> getLowBookPriseByCategory(List<Book> list) {
return list.stream()
.collect(Collectors.groupingBy(Book::getCategory,
Collectors.minBy(Comparator.comparingInt(Book::getPrice))));
}
// Верное решение
static Map<String, Integer> getLowBookPriseByCategory(List<Book> list) {
return list.stream()
.collect(Collectors.toMap(
Book::getCategory, Book::getPrice,
BinaryOperator.minBy(Comparator.naturalOrder())
));
}
}