Core-2: Streams & Optional Flashcards
Что такое Optional?
Optional — это контейнер для объекта, который может содержать или не содержать значение null.
Основное примененение: возвращаемое значение метода, когда возвращаемое значение может отсутствовать (null).
Нужен для:
- предотвращения NullPointerException, избегая BoilepPlate
- указать пользователю, что здесь может быть null
Такая обёртка является удобным средством , т.к. имеет некоторые функции высшего порядка, избавляющие от добавления повторяющихся if null/notNull проверок:
Optional<String> optional = Optional.of("hello"); optional.isPresent(); // true optional.ifPresent(s -> System.out.println(s.length())); // 5 optional.get(); // "hello" optional.orElse("ops..."); // "hello"
Как правило используется в качестве возвращаемого значения метода и использовать его стоит там где есть вероятность отсутствия возвращаемого значения.
Главные методы Optional<T>
-
public static<T> Optional<T> empty()
: Returns an empty Optional instance. -
public static <T> Optional<T> of(T value)
: Returns an Optional describing the given non-null value. Throws: NullPointerException – if value is null -
public static <T> Optional<T> ofNullable(T value)
: Returns an Optional describing the given value, if non-null, otherwise returns an empty Optional. Should only be used if NPE may be thrown. -
public T get()
: If a value is present, returns the value, otherwise throws NoSuchElementException. API Note:The preferred alternative to this method is -
public boolean isPresent():
If a value is present, returns true, otherwise false. -
public void ifPresent(Consumer<? super T> action)
: If a value is present, performs the given action with the value, otherwise does nothing. Throws:
NullPointerException – if value is present and the given action is null -
public Optional<T> filter(Predicate<? super T> predicate)
: If a value is present, and the value matches the given predicate, returns an Optional describing the value, otherwise returns an empty Optional. Throws: NullPointerException – if the predicate is null
- public <U> Optional<U> map(Function<? super T, ? extends U> mapper)
-
public <U> Optional<U> flatMap( Function<? super T, ? extends Optional<? extends U>> mapper)
: Dealing with nested optionals is the most common case when we need to use flatMap instead of map -
public T orElse(T other)
: If a value is present, returns the value, otherwise returns other.
- public T orElseGet(Supplier<? extends T> supplier)
: If a value is present, returns the value, otherwise returns other.
- public T orElseThrow()
: If a value is present, returns the value, otherwise throws NoSuchElementException
SINCE JAVA 9:
- public void ifPresentOrElse(Consumer<? super T> action,
Runnable emptyAction)
- public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
- public Stream<T> stream()
-
SINCE JAVA 10:
- orElseThrow()
SINCE JAVA 11:
- public boolean isEmpty()
If no value, returns true, otherwise false
Что такое Stream? Какие типы операций у него бывают и в чем их логика?
Интерфейс java.util.Stream представляет собой обертку над источником данных, с помощью которой можно параллельно или последовательно обрабатывать эти данные в функциональном стиле, не меняя сам источник данных.
Операции над стримами бывают или промежуточными (intermediate) или конечными (terminal). Конечные операции возвращают результат определенного типа, а промежуточные операции возвращают тот же стрим. Таким образом вы можете строить цепочки из несколько операций над одним и тем же стримом.
У стрима может быть сколько угодно вызовов промежуточных операций и последним вызов конечной операции. При этом все промежуточные операции выполняются лениво и пока не будет вызвана конечная операция никаких действий на самом деле не происходит (похоже на создание объекта Thread или Runnable, без вызова start()).
Поддерживают ли стримы Ассоциативные массивы?
Ассоциативные массивы (maps), например, HashMap, не поддерживаются.
Можно ли переиспользовать Stream и могут ли они операции исполняться паралельно?
Потоки не могут быть использованы повторно. Как только была вызвана какая-нибудь конечная операция, поток закрывается.
Операции над стримами могут выполняться как последовательно, так и параллельно.
Чем лучше пользоваться при работе со стримами примитивов?
Кроме универсальных объектных существуют особые виды стримов для работы с примитивными типами данных int, long и double: IntStream, LongStream и DoubleStream. Эти примитивные стримы работают так же, как и обычные объектные, но со следующими отличиями:
используют специализированные лямбда-выражения, например, IntFunction или IntPredicate вместо Function и Predicate;
поддерживают дополнительные конечные операции sum(), average(), mapToObj().
Какие существуют способы создания стрима?
1) Из коллекции:
~~~
Stream<String> fromCollection = Arrays.asList("x", "y", "z").stream();
~~~
2) Из набора значений:
~~~
Stream<String> fromValues = Stream.of("x", "y", "z");
~~~
3) Из массива:
~~~
- Stream<String> fromArray = Arrays.stream(new String[]{"x", "y", "z"});
~~~
4) Из файла (каждая строка в файле будет отдельным элементом в стриме):
~~~
Stream<String> fromFile = Files.lines(Paths.get("input.txt"));
~~~
5) Из строки:
~~~
IntStream fromString = "0123456789".chars();
~~~
6) С помощью Stream.builder():
~~~
Stream<String> fromBuilder = Stream.builder().add("z").add("y").add("z").build();
~~~
7) С помощью Stream.iterate() (бесконечный):
~~~
Stream<Integer> fromIterate = Stream.iterate(1, n -> n + 1);
~~~
8) С помощью Stream.generate() (бесконечный):
`Stream<String> fromGenerate = Stream.generate(() -> "0");`</String></Integer></String></String></String></String></String>
9) public static LongStream rangeClosed(long startInclusive,
final long endInclusive)
: range( start, excl).
Соединить 2 стрима:Stream<Integer> stream3 = Stream.concat(stream1, stream2);
SINCE JAVA 11:
~~~
- public Stream<String> lines() :
Stream<String> stream = "Breaking news\n
a new version of Java\ris coming soon!".lines();
~~~</String></String>
В чем разница между Collection и Stream?
Коллекции позволяют работать с элементами по-отдельности, тогда как стримы так делать не позволяют, но вместо этого предоставляют возможность выполнять функции над данными как над одним целым.
Также стоит отметить важность самой концепции сущностей: Collection - это прежде всего воплощение Структуры Данных. Например, Set не просто хранит в себе элементы, он реализует идею множества с уникальными элементами, тогда как Stream, это прежде всего абстракция необходимая для реализации конвейера вычислений, собственно, поэтому, результатом работы конвейера являются те или иные Структуры Данных или же результаты проверок/поиска и т.п.
Основные промежуточные методы Стримов.
-
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
: Returns a stream consisting of the results of applying the given function to the elements of this stream. <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
-
IntStream mapToInt(ToIntFunction<? super T> mapper)
: Returns an IntStream consisting of the results of applying the given function to the elements of this stream.mapToDouble / mapToLong
-
Stream<Integer> boxed()
: boxes int to Integer, used before collect()
- IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) : superList.stream().flatMapToInt(x -> x.stream() .mapToInt(u->u)).min()...
-
Stream<T> filter(Predicate<? super T> predicate);
: Returns a stream consisting of the elements of this stream that match the given predicate.
- Stream<T> limit(long maxSize)
: Returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length.
-
Stream<T> skip(long n)
: Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream -
Stream<T> sorted(Comparator<? super T> comparator)
: Returns a stream consisting of the elements of this stream, sorted according to the provided Comparator, or naturally if no Comparator provided. -
Stream<T> distinct();
: Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream. -
Stream<T> peek(Consumer<? super T> action)
: Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element (like forEach but intermediate)
SINCE JAVA 9:- default Stream<T> takeWhile(Predicate<? super T> predicate)
- default Stream<T> takeWhile(Predicate<? super T> predicate)
: suits for sorted streams, otherwise unpredictable.
Основные терминальные методы Стримов.
-
long count()
: Returns the count of elements in this stream -
Optional<T> findFirst() / findAny()
: Returns an Optional describing the first (or any) element of this stream, or an empty Optional if the stream is empty. boolean anyMatch(Predicate<? super T> predicate) / allMAtch/noneMatch
-
void forEach(Consumer<? super T> action)
: Performs an action for each element of this stream. This is a terminal operation. Optional<T> max / min (Comparator<? super T> comparator)
-
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
:
without identity returns Optional,
uses combiner when types of accumulator function mismatch:
~~~
int result = users.stream().reduce(
0, (partialAgeResult, user)-> partialAgeResult + user.getAge(),
Integer::sum);
~~~ -
<R, A> R collect(Collector<? super T, A, R> collector)
: Performs a mutable reduction operation on the elements of this stream using a Collector -
<A> A[] toArray(IntFunction<A[]> generator)
:
~~~
Person[] men = people.stream()
.filter(p -> p.getGender() == MALE)
.toArray(Person[]::new);
~~~
Primitive Streams:
- int sum()
: terminal, returns sum of els it this stream
- OptionalDouble average().orElse(0)
:
- OptionalInt min().getAsInt
: OptionalInt max().getAsInt
Для чего нужен метод collect() в стримах?
collect() является конечной операцией, которая используется для представление результата в виде коллекции или какой-либо другой структуры данных:
- <R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
or:
`<R, A> R collect(Collector<? super T, A, R> collector) ` :
что такое Collector?
Коллектор задает способ комбинирования элементов стрима в единое целое.
Collector<Тип_источника, Тип_аккумулятора, Тип_результата>
В классе Collectors реализовано несколько распространённых коллекторов:
- toList(), toCollection(), toSet() :
~~~
CopyOnWriteArrayList<Integer> numbers =Stream.of(7, 4, 9, 11, 4)
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
~~~</Integer>
- toMap (Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator mergeFunction, Supplier mapFactory) :
public Map<String, String> listToMap(List<Book> books) { return books.stream().collect(Collectors.toMap( Book::getYear, Function.identity(), (existing, replacer) ->existing, TreeMap::new));}
- partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream) : делит коллекцию на две части по соответствию условию, возвращает Map<Boolean, T> :
~~~
Map<Boolean, Long> map = Stream.of(1,2,3,4,5,).collect(
Collectors.partitioningBy(x->x > 3, Collectors.counting()));
~~~
- groupingBy(Predicate predicate, Supplier mapFactory, Collector downstream) - разделяет коллекцию на несколько частей и возвращает Map<N, T>>:
EnumMap<PostType, List<Post>> postsPerType = posts.stream() .collect(groupingBy(Post::getType, () -> new EnumMap<>(PostType.class), toList()));
**- summingInt(ToIntFunction<? super T> mapper),
summingDouble(…), summingLong(…) **
- averagingInt(ToIntFunction<? super T> mapper),
averagingDouble(), averagingLong() :
~~~
double ans = Stream.of(“3”, “4”, “5”).
.collect(Collectors.averagingInt( num -> Integer.parseInt(num)));
~~~
**- minBy / maxBy (Comparator<? super T> comparator) : **
~~~
Optional<Log> minDelayLog = logs.stream()
.collect(Collectors.minBy(Comparator.comparing(Log::getDelay)));
~~~
**- mapping / flatMapping (Function mapper, Collector downstream) - дополнительные преобразования значений для сложных Collector-ов.**</Log>
- STRING COLLECTORS:
- joining() / joining(CharSequence delimiter)
/ joining(CharSeq delim, CharSeq pref, CharSeq suff) :
~~~
Stream.of(“Java”, “Kotlin”)
.collect(Collectors.joining(“, “, “<”, “>”)); // <Java, Kotlin>
NB: there is String.join(“ “, “Functions”, “in”, “Java”);
~~~
SINCE JAVA 9:
**- filtering(Predicatepredicate, Collector downstream) : **
~~~
Map<String, Long> popularTweetCountByLang = tweets.stream()
.collect(groupingBy(Tweet::getLang, filtering(
tweet -> tweet.getLikeCount() > 100, counting())));
~~~
- flatMapping(Function mapper, Collector downstream) :
~~~
Map<String, Long> commentCountPerTweetLang = tweets.stream()
.collect(groupingBy(Tweet::getLang, flatMapping(
tweet -> tweet.getComments().stream(), counting())));
~~~
Так же существует возможность создания собственного коллектора через Collector.of():
~~~
Collector<String, List<String>, List<String>> toList =
Collector.of(
ArrayList::new,
List::add,
(l1, l2) -> { l1.addAll(l2); return l1; }
);
~~~</String></String>
Как создать параллельный стрим?
Какие у него доп. методы?
создать:
- list.parallelStream() вместо list.stream()
- existing stream to parallel: numbers.stream().parallel()…
доп. методы:
- isParallel()
- sequential()
NB: если в стриме встречаются обе команды (parallel() & seqential()), то в итоге применяется последняя.