929 lines
69 KiB
Markdown
929 lines
69 KiB
Markdown
[Вопросы для собеседования](README.md)
|
||
|
||
# Java 8
|
||
+ [Какие нововведения, появились в Java 8 и JDK 8?](#Какие-нововведения-появились-в-java-8-и-jdk-8)
|
||
+ [Что такое _«лямбда»_? Какова структура и особенности использования лямбда-выражения?](#Что-такое-лямбда-Какова-структура-и-особенности-использования-лямбда-выражения)
|
||
+ [К каким переменным есть доступ у лямбда-выражений?](#К-каким-переменным-есть-доступ-у-лямбда-выражений)
|
||
+ [Как отсортировать список строк с помощью лямбда-выражения?](#Как-отсортировать-список-строк-с-помощью-лямбда-выражения)
|
||
+ [Что такое «ссылка на метод»?](#Что-такое-ссылка-на-метод)
|
||
+ [Какие виды ссылок на методы вы знаете?](#Какие-виды-ссылок-на-методы-вы-знаете)
|
||
+ [Объясните выражение `System.out::println`.](#Объясните-выражение-systemoutprintln)
|
||
+ [Что такое «функциональные интерфейсы»?](#Что-такое-функциональные-интерфейсы)
|
||
+ [Для чего нужны функциональные интерфейсы `Function<T,R>`, `DoubleFunction<R>`, `IntFunction<R>` и `LongFunction<R>`?](#Для-чего-нужны-функциональные-интерфейсы-functiontr-doublefunctionr-intfunctionr-и-longfunctionr)
|
||
+ [Для чего нужны функциональные интерфейсы `UnaryOperator<T>`, `DoubleUnaryOperator`, `IntUnaryOperator` и `LongUnaryOperator`?](#Для-чего-нужны-функциональные-интерфейсы-unaryoperatort-doubleunaryoperator-intunaryoperator-и-longunaryoperator)
|
||
+ [Для чего нужны функциональные интерфейсы `BinaryOperator<T>`, `DoubleBinaryOperator`, `IntBinaryOperator` и `LongBinaryOperator`?](#Для-чего-нужны-функциональные-интерфейсы-binaryoperatort-doublebinaryoperator-intbinaryoperator-и-longbinaryoperator)
|
||
+ [Для чего нужны функциональные интерфейсы `Predicate<T>`, `DoublePredicate`, `IntPredicate` и `LongPredicate`?](#Для-чего-нужны-функциональные-интерфейсы-predicatet-doublepredicate-intpredicate-и-longpredicate)
|
||
+ [Для чего нужны функциональные интерфейсы `Consumer<T>`, `DoubleConsumer`, `IntConsumer` и `LongConsumer`?](#Для-чего-нужны-функциональные-интерфейсы-consumert-doubleconsumer-intconsumer-и-longconsumer)
|
||
+ [Для чего нужны функциональные интерфейсы `Supplier<T>`, `BooleanSupplier`, `DoubleSupplier`, `IntSupplier` и `LongSupplier`?](#Для-чего-нужны-функциональные-интерфейсы-suppliert--booleansupplier-doublesupplier-intsupplier-и-longsupplier)
|
||
+ [Для чего нужен функциональный интерфейс `BiConsumer<T,U>`?](#Для-чего-нужен-функциональный-интерфейс-biconsumertu)
|
||
+ [Для чего нужен функциональный интерфейс `BiFunction<T,U,R>`?](#Для-чего-нужен-функциональный-интерфейс-bifunctiontur)
|
||
+ [Для чего нужен функциональный интерфейс `BiPredicate<T,U>`?](#Для-чего-нужен-функциональный-интерфейс-bipredicatetu)
|
||
+ [Для чего нужны функциональные интерфейсы вида `_To_Function`?](#Для-чего-нужны-функциональные-интерфейсы-вида-tofunction)
|
||
+ [Для чего нужны функциональные интерфейсы `ToDoubleBiFunction<T,U>`, `ToIntBiFunction<T,U>` и `ToLongBiFunction<T,U>`?](#Для-чего-нужны-функциональные-интерфейсы-todoublebifunctiontu-tointbifunctiontu-и-tolongbifunctiontu)
|
||
+ [Для чего нужны функциональные интерфейсы `ToDoubleFunction<T>`, `ToIntFunction<T>` и `ToLongFunction<T>`?](#Для-чего-нужны-функциональные-интерфейсы-todoublefunctiont-tointfunctiont-и-tolongfunctiont)
|
||
+ [Для чего нужны функциональные интерфейсы `ObjDoubleConsumer<T>`, `ObjIntConsumer<T>` и `ObjLongConsumer<T>`?](#Для-чего-нужны-функциональные-интерфейсы-objdoubleconsumert-objintconsumert-и-objlongconsumert)
|
||
+ [Что такое `StringJoiner`?](#Что-такое-stringjoiner)
|
||
+ [Что такое `default` методы интрефейса?](#Что-такое-default-методы-интрефейса)
|
||
+ [Как вызывать `default` метод интерфейса в реализующем этот интерфейс классе?](#Как-вызывать-default-метод-интерфейса-в-реализующем-этот-интерфейс-классе)
|
||
+ [Что такое `static` метод интерфейса?](#Что-такое-static-метод-интерфейса)
|
||
+ [Как вызывать `static` метод интерфейса?](#Как-вызывать-static-метод-интерфейса)
|
||
+ [Что такое `Optional`?](#Что-такое-optional)
|
||
+ [Что такое `Stream`?](#Что-такое-stream)
|
||
+ [Какие существуют способы создания стрима?](#Какие-существуют-способы-создания-стрима)
|
||
+ [В чем разница между `Collection` и `Stream`?](#В-чем-разница-между-collection-и-stream)
|
||
+ [Для чего нужен метод `collect()` в стримах?](#Для-чего-нужен-метод-collect-в-стримах)
|
||
+ [Для чего в стримах применяются методы `forEach()` и `forEachOrdered()`?](#Для-чего-в-стримах-применяются-методы-foreach-и-foreachordered)
|
||
+ [Для чего в стримах предназначены методы `map()` и `mapToInt()`, `mapToDouble()`, `mapToLong()`?](#Для-чего-в-стримах-предназначены-методы-map-и-maptoint-maptodouble-maptolong)
|
||
+ [Какова цель метода `filter()` в стримах?](#Какова-цель-метода-filter-в-стримах)
|
||
+ [Для чего в стримах предназначен метод `limit()`?](#Для-чего-в-стримах-предназначен-метод-limit)
|
||
+ [Для чего в стримах предназначен метод `sorted()`?](#Для-чего-в-стримах-предназначен-метод-sorted)
|
||
+ [Для чего в стримах предназначены методы `flatMap()`, `flatMapToInt()`, `flatMapToDouble()`, `flatMapToLong()`?](#Для-чего-в-стримах-предназначены-методы-flatmap-flatmaptoint-flatmaptodouble-flatmaptolong)
|
||
+ [Расскажите о параллельной обработке в Java 8.](#Расскажите-о-параллельной-обработке-в-java-8)
|
||
+ [Какие конечные методы работы со стримами вы знаете?](#Какие-конечные-методы-работы-со-стримами-вы-знаете)
|
||
+ [Какие промежуточные методы работы со стримами вы знаете?](#Какие-промежуточные-методы-работы-со-стримами-вы-знаете)
|
||
+ [Как вывести на экран 10 случайных чисел, используя `forEach()`?](#Как-вывести-на-экран-10-случайных-чисел-используя-foreach)
|
||
+ [Как можно вывести на экран уникальные квадраты чисел используя метод `map()`?](#Как-можно-вывести-на-экран-уникальные-квадраты-чисел-используя-метод-map)
|
||
+ [Как вывести на экран количество пустых строк с помощью метода `filter()`?](#Как-вывести-на-экран-количество-пустых-строк-с-помощью-метода-filter)
|
||
+ [Как вывести на экран 10 случайных чисел в порядке возрастания?](#Как-вывести-на-экран-10-случайных-чисел-в-порядке-возрастания)
|
||
+ [Как найти максимальное число в наборе?](#Как-найти-максимальное-число-в-наборе)
|
||
+ [Как найти минимальное число в наборе?](#Как-найти-минимальное-число-в-наборе)
|
||
+ [Как получить сумму всех чисел в наборе?](#Как-получить-сумму-всех-чисел-в-наборе)
|
||
+ [Как получить среднее значение всех чисел?](#Как-получить-среднее-значение-всех-чисел)
|
||
+ [Какие дополнительные методы для работы с ассоциативными массивами (maps) появились в Java 8?](#Какие-дополнительные-методы-для-работы-с-ассоциативными-массивами-maps-появились-в-java-8)
|
||
+ [Что такое `LocalDateTime`?](#Что-такое-localdatetime)
|
||
+ [Что такое `ZonedDateTime`?](#Что-такое-zoneddatetime)
|
||
+ [Как получить текущую дату с использованием Date Time API из Java 8?](#Как-получить-текущую-дату-с-использованием-date-time-api-из-java-8)
|
||
+ [Как добавить 1 неделю, 1 месяц, 1 год, 10 лет к текущей дате с использованием Date Time API?](#Как-добавить-1-неделю-1-месяц-1-год-10-лет-к-текущей-дате-с-использованием-date-time-api)
|
||
+ [Как получить следующий вторник используя Date Time API?](#Как-получить-следующий-вторник-используя-date-time-api)
|
||
+ [Как получить вторую субботу текущего месяца используя Date Time API?](#Как-получить-вторую-субботу-текущего-месяца-используя-date-time-api)
|
||
+ [Как получить текущее время с точностью до миллисекунд используя Date Time API?](#Как-получить-текущее-время-с-точностью-до-миллисекунд-используя-date-time-api)
|
||
+ [Как получить текущее время по местному времени с точностью до миллисекунд используя Date Time API?](#Как-получить-текущее-время-по-местному-времени-с-точностью-до-миллисекунд-используя-date-time-api)
|
||
+ [Как определить повторяемую аннотацию?](#Как-определить-повторяемую-аннотацию)
|
||
+ [Что такое `Nashorn`?](#Что-такое-nashorn)
|
||
+ [Что такое `jjs`?](#Что-такое-jjs)
|
||
+ [Какой класс появился в Java 8 для кодирования/декодирования данных?](#Какой-класс-появился-в-java-8-для-кодированиядекодирования-данных)
|
||
+ [Как создать Base64 кодировщик и декодировщик?](#Как-создать-base64-кодировщик-и-декодировщик)
|
||
|
||
## Какие нововведения, появились в Java 8 и JDK 8?
|
||
+ Методы интерфейсов по умолчанию;
|
||
+ Лямбда-выражения;
|
||
+ Функциональные интерфейсы;
|
||
+ Ссылки на методы и конструкторы;
|
||
+ Повторяемые аннотации;
|
||
+ Аннотации на типы данных;
|
||
+ Рефлексия для параметров методов;
|
||
+ _Stream API_ для работы с коллекциями;
|
||
+ Параллельная сортировка массивов;
|
||
+ Новое API для работы с датами и временем;
|
||
+ Новый движок JavaScript _Nashorn_;
|
||
+ Добавлено несколько новых классов для потокобезопасной работы;
|
||
+ Добавлен новый API для `Calendar` и `Locale`;
|
||
+ Добавлена поддержка _Unicode 6.2.0_;
|
||
+ Добавлен стандартный класс для работы с _Base64_;
|
||
+ Добавлена поддержка беззнаковой арифметики;
|
||
+ Улучшена производительность конструктора `java.lang.String(byte[], *)` и метода `java.lang.String.getBytes()`;
|
||
+ Новая реализация `AccessController.doPrivileged`, позволяющая устанавливать подмножество привилегий без необходимости проверки всех остальных уровней доступа;
|
||
+ _Password-based_ алгоритмы стали более устойчивыми;
|
||
+ Добавлена поддержка _SSL/TLS Server Name Indication (NSI)_ в _JSSE Server_;
|
||
+ Улучшено хранилище ключей (KeyStore);
|
||
+ Добавлен алгоритм _SHA-224_;
|
||
+ Удален мост _JDBC - ODBC_;
|
||
+ Удален _PermGen_, изменен способ хранения мета-данных классов;
|
||
+ Возможность создания профилей для платформы Java SE, которые включают в себя не всю платформу целиком, а некоторую ее часть;
|
||
+ Инструментарий
|
||
+ Добавлена утилита `jjs` для использования _JavaScript Nashorn_;
|
||
+ Команда `java` может запускать _JavaFX_ приложения;
|
||
+ Добавлена утилита `jdeps` для анализа _.class_-файлов.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое _«лямбда»_? Какова структура и особенности использования лямбда-выражения?
|
||
__Лямбда__ представляет собой набор инструкций, которые можно выделить в отдельную переменную и затем многократно вызвать в различных местах программы.
|
||
|
||
Основу лямбда-выражения составляет _лямбда-оператор_, который представляет стрелку `->`. Этот оператор разделяет лямбда-выражение на две части: левая часть содержит список параметров выражения, а правая, собственно, представляет тело лямбда-выражения, где выполняются все действия.
|
||
|
||
Лямбда-выражение не выполняется само по себе, а образует реализацию метода, определенного в функциональном интерфейсе. При этом важно, что функциональный интерфейс должен содержать только один единственный метод без реализации.
|
||
|
||
```java
|
||
interface Operationable {
|
||
int calculate(int x, int y);
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
Operationable operation = (x, y) -> x + y;
|
||
int result = operation.calculate(10, 20);
|
||
System.out.println(result); //30
|
||
}
|
||
```
|
||
|
||
По факту лямбда-выражения являются в некотором роде сокращенной формой внутренних анонимных классов, которые ранее применялись в Java.
|
||
|
||
+ _Отложенное выполнение (deferred execution) лямбда-выражения_- определяется один раз в одном месте программы, вызываются при необходимости, любое количество раз и в произвольном месте программы.
|
||
|
||
+ _Параметры лямбда-выражения_ должны соответствовать по типу параметрам метода функционального интерфейса:
|
||
|
||
```java
|
||
operation = (int x, int y) -> x + y;
|
||
//При написании самого лямбда-выражения тип параметров разрешается не указывать:
|
||
(x, y) -> x + y;
|
||
//Если метод не принимает никаких параметров, то пишутся пустые скобки, например,
|
||
() -> 30 + 20;
|
||
//Если метод принимает только один параметр, то скобки можно опустить:
|
||
n -> n * n;
|
||
```
|
||
|
||
+ _Конечные лямбда-выражения_ не обязаны возвращать какое-либо значение.
|
||
|
||
```java
|
||
interface Printable {
|
||
void print(String s);
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
Printable printer = s -> System.out.println(s);
|
||
printer.print("Hello, world");
|
||
}
|
||
```
|
||
|
||
+ _Блочные лямбда-выражения_ обрамляются фигурными скобками. В блочных лямбда-выражениях можно использовать внутренние вложенные блоки, циклы, конструкции `if`, `switch`, создавать переменные и т.д. Если блочное лямбда-выражение должно возвращать значение, то явным образом применяется оператор `return`:
|
||
|
||
```java
|
||
Operationable operation = (int x, int y) -> {
|
||
if (y == 0) {
|
||
return 0;
|
||
}
|
||
else {
|
||
return x / y;
|
||
}
|
||
};
|
||
```
|
||
|
||
+ _Передача лямбда-выражения в качестве параметра метода_:
|
||
|
||
```java
|
||
interface Condition {
|
||
boolean isAppropriate(int n);
|
||
}
|
||
|
||
private static int sum(int[] numbers, Condition condition) {
|
||
int result = 0;
|
||
for (int i : numbers) {
|
||
if (condition.isAppropriate(i)) {
|
||
result += i;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println(sum(new int[] {0, 1, 0, 3, 0, 5, 0, 7, 0, 9}, (n) -> n != 0));
|
||
}
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## К каким переменным есть доступ у лямбда-выражений?
|
||
Доступ к переменным внешней области действия из лямбда-выражения очень схож к доступу из анонимных объектов. Можно ссылаться на:
|
||
|
||
+ неизменяемые (_effectively final_ - не обязательно помеченные как `final`) локальные переменные;
|
||
+ поля класса;
|
||
+ статические переменные.
|
||
|
||
К методам по умолчанию реализуемого функционального интерфейса обращаться внутри лямбда-выражения запрещено.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как отсортировать список строк с помощью лямбда-выражения?
|
||
```java
|
||
public static List<String> sort(List<String> list){
|
||
Collections.sort(list, (a, b) -> a.compareTo(b));
|
||
return list;
|
||
}
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое «ссылка на метод»?
|
||
Если существующий в классе метод уже делает все, что необходимо, то можно воспользоваться механизмом __method reference (ссылка на метод)__ для непосредственной передачи этого метода. Такая ссылка передается в виде:
|
||
|
||
+ `имя_класса::имя_статического_метода` для статического метода;
|
||
+ `объект_класса::имя_метода` для метода экземпляра;
|
||
+ `название_класса::new` для конструктора.
|
||
|
||
Результат будет в точности таким же, как в случае определения лямбда-выражения, которое вызывает этот метод.
|
||
|
||
```java
|
||
private interface Measurable {
|
||
public int length(String string);
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
Measurable a = String::length;
|
||
System.out.println(a.length("abc"));
|
||
}
|
||
```
|
||
|
||
Ссылки на методы потенциально более эффективны, чем использование лямбда-выражений. Кроме того, они предоставляют компилятору более качественную информацию о типе и при возможности выбора между использованием ссылки на существующий метод и использованием лямбда-выражения, следует всегда предпочитать использование ссылки на метод.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Какие виды ссылок на методы вы знаете?
|
||
+ на статический метод;
|
||
+ на метод экземпляра;
|
||
+ на конструкторе.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Объясните выражение `System.out::println`.
|
||
Данное выражение иллюстрирует механизм _instance method reference_: передачи ссылки на метод `println()` статического поля `out` класса `System`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое «функциональные интерфейсы»?
|
||
__Функциональный интерфейс__ - это интерфейс, который определяет только один абстрактный метод.
|
||
|
||
Чтобы точно определить интерфейс как функциональный, добавлена аннотация `@FunctionalInterface`, работающая по принципу `@Override`. Она обозначит замысел и не даст определить второй абстрактный метод в интерфейсе.
|
||
|
||
Интерфейс может включать сколько угодно `default` методов и при этом оставаться функциональным, потому что `default` методы - не абстрактные.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `Function<T,R>`, `DoubleFunction<R>`, `IntFunction<R>` и `LongFunction<R>`?
|
||
__`Function<T, R>`__ - интерфейс, с помощью которого реализуется функция, получающая на вход экземпляр класса `T` и возвращающая на выходе экземпляр класса `R`.
|
||
|
||
Методы по умолчанию могут использоваться для построения цепочек вызовов (`compose`, `andThen`).
|
||
|
||
```java
|
||
Function<String, Integer> toInteger = Integer::valueOf;
|
||
Function<String, String> backToString = toInteger.andThen(String::valueOf);
|
||
backToString.apply("123"); // "123"
|
||
```
|
||
|
||
+ `DoubleFunction<R>` - функция, получающая на вход `Double` и возвращающая на выходе экземпляр класса `R`;
|
||
+ `IntFunction<R>` - функция, получающая на вход `Integer` и возвращающая на выходе экземпляр класса `R`;
|
||
+ `LongFunction<R>` - функция, получающая на вход `Long` и возвращающая на выходе экземпляр класса `R`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `UnaryOperator<T>`, `DoubleUnaryOperator`, `IntUnaryOperator` и `LongUnaryOperator`?
|
||
__`UnaryOperator<T>` (унарный оператор)__ принимает в качестве параметра объект типа `T`, выполняет над ними операции и возвращает результат операций в виде объекта типа `T`:
|
||
|
||
```java
|
||
UnaryOperator<Integer> operator = x -> x * x;
|
||
System.out.println(operator.apply(5)); // 25
|
||
```
|
||
|
||
+ `DoubleUnaryOperator` - унарный оператор, получающий на вход `Double`;
|
||
+ `IntUnaryOperator` - унарный оператор, получающий на вход `Integer`;
|
||
+ `LongUnaryOperator` - унарный оператор, получающий на вход `Long`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `BinaryOperator<T>`, `DoubleBinaryOperator`, `IntBinaryOperator` и `LongBinaryOperator`?
|
||
__`BinaryOperator<T>` (бинарный оператор)__ - интерфейс, с помощью которого реализуется функция, получающая на вход два экземпляра класса `T` и возвращающая на выходе экземпляр класса `T`.
|
||
```java
|
||
BinaryOperator<Integer> operator = (a, b) -> a + b;
|
||
System.out.println(operator.apply(1, 2)); // 3
|
||
```
|
||
|
||
+ `DoubleBinaryOperator` - бинарный оператор, получающий на вход `Double`;
|
||
+ `IntBinaryOperator` - бинарный оператор, получающий на вход `Integer`;
|
||
+ `LongBinaryOperator` - бинарный оператор, получающий на вход `Long`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `Predicate<T>`, `DoublePredicate`, `IntPredicate` и `LongPredicate`?
|
||
__`Predicate<T>` (предикат)__ - интерфейс, с помощью которого реализуется функция, получающая на вход экземпляр класса `T` и возвращающая на выходе значение типа `boolean`.
|
||
|
||
Интерфейс содержит различные методы по умолчанию, позволяющие строить сложные условия (`and`, `or`, `negate`).
|
||
|
||
```java
|
||
Predicate<String> predicate = (s) -> s.length() > 0;
|
||
predicate.test("foo"); // true
|
||
predicate.negate().test("foo"); // false
|
||
```
|
||
|
||
+ `DoublePredicate` - предикат, получающий на вход `Double`;
|
||
+ `IntPredicate` - предикат, получающий на вход `Integer`;
|
||
+ `LongPredicate` - предикат, получающий на вход `Long`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `Consumer<T>`, `DoubleConsumer`, `IntConsumer` и `LongConsumer`?
|
||
__`Consumer<T>` (потребитель)__ - интерфейс, с помощью которого реализуется функция, которая получает на вход экземпляр класса `T`, производит с ним некоторое действие и ничего не возвращает.
|
||
|
||
```java
|
||
Consumer<String> hello = (name) -> System.out.println("Hello, " + name);
|
||
hello.accept("world");
|
||
```
|
||
|
||
+ `DoubleConsumer` - потребитель, получающий на вход `Double`;
|
||
+ `IntConsumer` - потребитель, получающий на вход `Integer`;
|
||
+ `LongConsumer` - потребитель, получающий на вход `Long`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `Supplier<T>`, `BooleanSupplier`, `DoubleSupplier`, `IntSupplier` и `LongSupplier`?
|
||
__`Supplier<T>` (поставщик)__ - интерфейс, с помощью которого реализуется функция, ничего не принимающая на вход, но возвращающая на выход результат класса `T`;
|
||
|
||
```java
|
||
Supplier<LocalDateTime> now = LocalDateTime::now;
|
||
now.get();
|
||
```
|
||
|
||
+ `DoubleSupplier` - поставщик, возвращающий `Double`;
|
||
+ `IntSupplier` - поставщик, возвращающий `Integer`;
|
||
+ `LongSupplier` - поставщик, возвращающий `Long`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужен функциональный интерфейс `BiConsumer<T,U>`?
|
||
__`BiConsumer<T,U>`__ представляет собой операцию, которая принимает два аргумента классов `T` и `U` производит с ними некоторое действие и ничего не возвращает.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужен функциональный интерфейс `BiFunction<T,U,R>`?
|
||
__`BiFunction<T,U,R>`__ представляет собой операцию, которая принимает два аргумента классов `T` и `U` и возвращающая результат класса `R`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужен функциональный интерфейс `BiPredicate<T,U>`?
|
||
__`BiPredicate<T,U>`__ представляет собой операцию, которая принимает два аргумента классов `T` и `U` и возвращающая результат типа `boolean`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы вида `_To_Function`?
|
||
+ `DoubleToIntFunction` - операция, принимающая аргумент класса `Double` и возвращающая результат типа `Integer`;
|
||
+ `DoubleToLongFunction` - операция, принимающая аргумент класса `Double` и возвращающая результат типа `Long`;
|
||
+ `IntToDoubleFunction` - операция, принимающая аргумент класса `Integer` и возвращающая результат типа `Double`;
|
||
+ `IntToLongFunction` - операция, принимающая аргумент класса `Integer` и возвращающая результат типа `Long`;
|
||
+ `LongToDoubleFunction` - операция, принимающая аргумент класса `Long` и возвращающая результат типа `Double`;
|
||
+ `LongToIntFunction` - операция, принимающая аргумент класса `Long` и возвращающая результат типа `Integer`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `ToDoubleBiFunction<T,U>`, `ToIntBiFunction<T,U>` и `ToLongBiFunction<T,U>`?
|
||
+ `ToDoubleBiFunction<T,U>` - операция принимающая два аргумента классов `T` и `U` и возвращающая результат типа `Double`;
|
||
+ `ToLongBiFunction<T,U>` - операция принимающая два аргумента классов `T` и `U` и возвращающая результат типа `Long`;
|
||
+ `ToIntBiFunction<T,U>` - операция принимающая два аргумента классов `T` и `U` и возвращающая результат типа `Integer`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `ToDoubleFunction<T>`, `ToIntFunction<T>` и `ToLongFunction<T>`?
|
||
+ `ToDoubleFunction<T>` - операция, принимающая аргумент класса `T` и возвращающая результат типа `Double`;
|
||
+ `ToLongFunction<T>` - операция, принимающая аргумент класса `T` и возвращающая результат типа `Long`;
|
||
+ `ToIntFunction<T>` - операция, принимающая аргумент класса `T` и возвращающая результат типа `Integer`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужны функциональные интерфейсы `ObjDoubleConsumer<T>`, `ObjIntConsumer<T>` и `ObjLongConsumer<T>`?
|
||
+ `ObjDoubleConsumer<T>` - операция, которая принимает два аргумента классов `T` и `Double`, производит с ними некоторое действие и ничего не возвращает;
|
||
+ `ObjLongConsumer<T>` - операция, которая принимает два аргумента классов `T` и `Long`, производит с ними некоторое действие и ничего не возвращает;
|
||
+ `ObjIntConsumer<T>` - операция, которая принимает два аргумента классов `T` и `Integer`, производит с ними некоторое действие и ничего не возвращает.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `StringJoiner`?
|
||
Класс `StringJoiner` используется, чтобы создать последовательность строк, разделенных разделителем с возможностью присоединить к полученной строке префикс и суффикс:
|
||
|
||
```java
|
||
StringJoiner joiner = new StringJoiner(".", "prefix-", "-suffix");
|
||
for (String s : "Hello the brave world".split(" ")) {
|
||
joiner.add(s);
|
||
}
|
||
System.out.println(joiner); //prefix-Hello.the.brave.world-suffix
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `default` методы интрефейса?
|
||
Java 8 позволяет добавлять неабстрактные реализации методов в интерфейс, используя ключевое слово `default`:
|
||
|
||
```java
|
||
interface Example {
|
||
int process(int a);
|
||
default void show() {
|
||
System.out.println("default show()");
|
||
}
|
||
}
|
||
```
|
||
|
||
+ Если класс реализует интерфейс, он может, но не обязан, реализовать методы по-умолчанию, уже реализованные в интерфейсе. Класс наследует реализацию по умолчанию.
|
||
+ Если некий класс реализует несколько интерфейсов, которые имеют одинаковый метод по умолчанию, то класс должен реализовать метод с совпадающей сигнатурой самостоятельно. Ситуация аналогична, если один интерфейс имеет метод по умолчанию, а в другом этот же метод является абстрактным - никакой реализации по умолчанию классом не наследуется.
|
||
+ Метод по умолчанию не может переопределить метод класса `java.lang.Object`.
|
||
+ Помогают реализовывать интерфейсы без страха нарушить работу других классов.
|
||
+ Позволяют избежать создания служебных классов, так как все необходимые методы могут быть представлены в самих интерфейсах.
|
||
+ Дают свободу классам выбрать метод, который нужно переопределить.
|
||
+ Одной из основных причин внедрения методов по умолчанию является возможность коллекций в Java 8 использовать лямбда-выражения.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как вызывать `default` метод интерфейса в реализующем этот интерфейс классе?
|
||
Используя ключевое слово `super` вместе с именем интерфейса:
|
||
|
||
```java
|
||
interface Paper {
|
||
default void show() {
|
||
System.out.println("default show()");
|
||
}
|
||
}
|
||
|
||
class Licence implements Paper {
|
||
public void show() {
|
||
Paper.super.show();
|
||
}
|
||
}
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `static` метод интерфейса?
|
||
Статические методы интерфейса похожи на методы по умолчанию, за исключением того, что для них отсутствует возможность переопределения в классах, реализующих интерфейс.
|
||
|
||
+ Статические методы в интерфейсе являются частью интерфейса без возможности переопределить их для объектов класса реализации;
|
||
+ Методы класса `java.lang.Object` нельзя переопределить как статические;
|
||
+ Статические методы в интерфейсе используются для обеспечения вспомогательных методов, например, проверки на null, сортировки коллекций и т.д.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как вызывать `static` метод интерфейса?
|
||
Используя имя интерфейса:
|
||
|
||
```java
|
||
interface Paper {
|
||
static void show() {
|
||
System.out.println("static show()");
|
||
}
|
||
}
|
||
|
||
class Licence {
|
||
public void showPaper() {
|
||
Paper.show();
|
||
}
|
||
}
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `Optional`?
|
||
Опциональное значение `Optional` — это контейнер для объекта, который может содержать или не содержать значение `null`. Такая обёртка является удобным средством предотвращения `NullPointerException`, т.к.
|
||
имеет некоторые функции высшего порядка, избавляющие от добавления повторяющихся `if null/notNull` проверок:
|
||
|
||
```java
|
||
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"
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `Stream`?
|
||
Интерфейс `java.util.Stream` представляет собой последовательность элементов, над которой можно производить различные операции.
|
||
|
||
Операции над стримами бывают или _промежуточными (intermediate)_ или _конечными (terminal)_. Конечные операции возвращают результат определенного типа, а промежуточные операции возвращают тот же стрим. Таким образом вы можете строить цепочки из несколько операций над одним и тем же стримом.
|
||
|
||
У стрима может быть сколько угодно вызовов промежуточных операций и последним вызов конечной операции. При этом все промежуточные операции выполняются лениво и пока не будет вызвана конечная операция никаких действий на самом деле не происходит (похоже на создание объекта `Thread` или `Runnable`, без вызова `start()`).
|
||
|
||
Стримы создаются на основе каких-либо источников, например классов из `java.util.Collection`.
|
||
|
||
Ассоциативные массивы (maps), например, `HashMap`, не поддерживаются.
|
||
|
||
Операции над стримами могут выполняться как последовательно, так и параллельно.
|
||
|
||
Потоки не могут быть использованы повторно. Как только была вызвана какая-нибудь конечная операция, поток закрывается.
|
||
|
||
Кроме универсальных объектных существуют особые виды стримов для работы с примитивными типами данных `int`, `long` и `double`: `IntStream`, `LongStream` и `DoubleStream`. Эти примитивные стримы работают так же, как и обычные объектные, но со следующими отличиями:
|
||
|
||
+ используют специализированные лямбда-выражения, например, `IntFunction` или `IntPredicate` вместо `Function` и `Predicate`;
|
||
+ поддерживают дополнительные конечные операции `sum()`, `average()`, `mapToObj()`.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Какие существуют способы создания стрима?
|
||
1. Из коллекции:
|
||
```java
|
||
Stream<String> fromCollection = Arrays.asList("x", "y", "z").stream();
|
||
```
|
||
2. Из набора значений:
|
||
```java
|
||
Stream<String> fromValues = Stream.of("x", "y", "z");
|
||
```
|
||
3. Из массива:
|
||
```java
|
||
Stream<String> fromArray = Arrays.stream(new String[]{"x", "y", "z"});
|
||
```
|
||
4. Из файла (каждая строка в файле будет отдельным элементом в стриме):
|
||
```java
|
||
Stream<String> fromFile = Files.lines(Paths.get("input.txt"));
|
||
```
|
||
5. Из строки:
|
||
```java
|
||
IntStream fromString = "0123456789".chars();
|
||
```
|
||
6. С помощью `Stream.builder()`:
|
||
```java
|
||
Stream<String> fromBuilder = Stream.builder().add("z").add("y").add("z").build();
|
||
```
|
||
7. С помощью `Stream.iterate()` (бесконечный):
|
||
```java
|
||
Stream<Integer> fromIterate = Stream.iterate(1, n -> n + 1);
|
||
```
|
||
8. С помощью `Stream.generate()` (бесконечный):
|
||
```java
|
||
Stream<String> fromGenerate = Stream.generate(() -> "0");
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## В чем разница между `Collection` и `Stream`?
|
||
Коллекции позволяют работать с элементами по-отдельности, тогда как стримы так делать не позволяют, но вместо этого предоставляют возможность выполнять функции над данными как над одним целым.
|
||
|
||
Также стоит отметить важность самой концепции сущностей: `Collection` - это прежде всего воплощение _Структуры Данных_. Например, `Set` не просто хранит в себе элементы, он реализует идею множества с уникальными элементами,
|
||
тогда как `Stream`, это прежде всего абстракция необходимая для реализации _конвейера вычислений_, собственно, поэтому, результатом работы конвейера являются те или иные _Структуры Данных_ или же результаты проверок/поиска и т.п.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего нужен метод `collect()` в стримах?
|
||
Метод `collect()` является конечной операцией, которая используется для представления результата в виде коллекции или какой-либо другой структуры данных.
|
||
|
||
`collect()` принимает на вход `Collector<Тип_источника, Тип_аккумулятора, Тип_результата>`, который содержит четыре этапа: _supplier_ - инициализация аккумулятора, _accumulator_ - обработка каждого элемента, _combiner_ - соединение двух аккумуляторов при параллельном выполнении, _[finisher]_ - необязательный метод последней обработки аккумулятора. В Java 8 в классе `Collectors` реализовано несколько распространённых коллекторов:
|
||
|
||
+ `toList()`, `toCollection()`, `toSet()` - представляют стрим в виде списка, коллекции или множества;
|
||
+ `toConcurrentMap()`, `toMap()` - позволяют преобразовать стрим в `Map`;
|
||
+ `averagingInt()`, `averagingDouble()`, `averagingLong()` - возвращают среднее значение;
|
||
+ `summingInt()`, `summingDouble()`, `summingLong()` - возвращает сумму;
|
||
+ `summarizingInt()`, `summarizingDouble()`, `summarizingLong()` - возвращают `SummaryStatistics` с разными агрегатными значениями;
|
||
+ `partitioningBy()` - разделяет коллекцию на две части по соответствию условию и возвращает их как `Map<Boolean, List>`;
|
||
+ `groupingBy()` - разделяет коллекцию на несколько частей и возвращает `Map<N, List<T>>`;
|
||
+ `mapping()` - дополнительные преобразования значений для сложных `Collector`-ов.
|
||
|
||
Так же существует возможность создания собственного коллектора через `Collector.of()`:
|
||
|
||
```java
|
||
Collector<String, List<String>, List<String>> toList = Collector.of(
|
||
ArrayList::new,
|
||
List::add,
|
||
(l1, l2) -> { l1.addAll(l2); return l1; }
|
||
);
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего в стримах применяются методы `forEach()` и `forEachOrdered()`?
|
||
+ `forEach()` применяет функцию к каждому объекту стрима, порядок при параллельном выполнении не гарантируется;
|
||
+ `forEachOrdered()` применяет функцию к каждому объекту стрима с сохранением порядка элементов.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего в стримах предназначены методы `map()` и `mapToInt()`, `mapToDouble()`, `mapToLong()`?
|
||
Метод `map()` является промежуточной операцией, которая заданным образом преобразует каждый элемент стрима.
|
||
|
||
`mapToInt()`, `mapToDouble()`, `mapToLong()` - аналоги `map()`, возвращающие соответствующий числовой стрим (то есть стрим из числовых примитивов):
|
||
|
||
```java
|
||
Stream
|
||
.of("12", "22", "4", "444", "123")
|
||
.mapToInt(Integer::parseInt)
|
||
.toArray(); //[12, 22, 4, 444, 123]
|
||
```
|
||
[к оглавлению](#java-8)
|
||
|
||
## Какова цель метода `filter()` в стримах?
|
||
Метод `filter()` является промежуточной операцией принимающей предикат, который фильтрует все элементы, возвращая только те, что соответствуют условию.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего в стримах предназначен метод `limit()`?
|
||
Метод `limit()` является промежуточной операцией, которая позволяет ограничить выборку определенным количеством первых элементов.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего в стримах предназначен метод `sorted()`?
|
||
Метод `sorted()` является промежуточной операцией, которая позволяет сортировать значения либо в натуральном порядке, либо задавая `Comparator`.
|
||
|
||
Порядок элементов в исходной коллекции остается нетронутым - `sorted()` всего лишь создает его отсортированное представление.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Для чего в стримах предназначены методы `flatMap()`, `flatMapToInt()`, `flatMapToDouble()`, `flatMapToLong()`?
|
||
Метод `flatMap()` похож на map, но может создавать из одного элемента несколько. Таким образом, каждый объект будет преобразован в ноль, один или несколько других объектов, поддерживаемых потоком. Наиболее очевидный способ применения этой операции — преобразование элементов контейнера при помощи функций, которые возвращают контейнеры.
|
||
|
||
```java
|
||
Stream
|
||
.of("H e l l o", "w o r l d !")
|
||
.flatMap((p) -> Arrays.stream(p.split(" ")))
|
||
.toArray(String[]::new);//["H", "e", "l", "l", "o", "w", "o", "r", "l", "d", "!"]
|
||
```
|
||
|
||
`flatMapToInt()`, `flatMapToDouble()`, `flatMapToLong()` - это аналоги `flatMap()`, возвращающие соответствующий числовой стрим.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Расскажите о параллельной обработке в Java 8.
|
||
Стримы могут быть последовательными и параллельными. Операции над последовательными стримами выполняются в одном потоке процессора, над параллельными — используя несколько потоков процессора. Параллельные стримы используют общий `ForkJoinPool` доступный через статический `ForkJoinPool.commonPool()` метод. При этом, если окружение не является многоядерным, то поток будет выполняться как последовательный. Фактически применение параллельных стримов сводится к тому, что данные в стримах будут разделены на части, каждая часть обрабатывается на отдельном ядре процессора, и в конце эти части соединяются, и над ними выполняются конечные операции.
|
||
|
||
Для создания параллельного потока из коллекции можно также использовать метод `parallelStream()` интерфейса `Collection`.
|
||
|
||
Чтобы сделать обычный последовательный стрим параллельным, надо вызвать у объекта `Stream` метод `parallel()`. Метод `isParallel()` позволяет узнать является ли стрим параллельным.
|
||
|
||
С помощью, методов `parallel()` и `sequential()` можно определять какие операции могут быть параллельными, а какие только последовательными. Так же из любого последовательного стрима можно сделать параллельный и наоборот:
|
||
|
||
```java
|
||
collection
|
||
.stream()
|
||
.peek(...) // операция последовательна
|
||
.parallel()
|
||
.map(...) // операция может выполняться параллельно,
|
||
.sequential()
|
||
.reduce(...) // операция снова последовательна
|
||
```
|
||
|
||
Как правило, элементы передаются в стрим в том же порядке, в котором они определены в источнике данных. При работе с параллельными стримами система сохраняет порядок следования элементов. Исключение составляет метод `forEach()`, который может выводить элементы в произвольном порядке. И чтобы сохранить порядок следования, необходимо применять метод `forEachOrdered()`.
|
||
|
||
Критерии, которые могут повлиять на производительность в параллельных стримах:
|
||
|
||
+ Размер данных - чем больше данных, тем сложнее сначала разделять данные, а потом их соединять.
|
||
+ Количество ядер процессора. Теоретически, чем больше ядер в компьютере, тем быстрее программа будет работать. Если на машине одно ядро, нет смысла применять параллельные потоки.
|
||
+ Чем проще структура данных, с которой работает поток, тем быстрее будут происходить операции. Например, данные из `ArrayList` легко использовать, так как структура данной коллекции предполагает последовательность несвязанных данных. А вот коллекция типа `LinkedList` - не лучший вариант, так как в последовательном списке все элементы связаны с предыдущими/последующими. И такие данные трудно распараллелить.
|
||
+ Над данными примитивных типов операции будут производиться быстрее, чем над объектами классов.
|
||
+ Крайне не рекомендуется использовать параллельные стримы для скольких-нибудь долгих операций (например, сетевых соединений), так как все параллельные стримы работают c одним ForkJoinPool, то такие долгие операции могут остановить работу всех параллельных стримов в JVM из-за отсутствия доступных потоков в пуле, т.е. параллельные стримы стоит использовать лишь для коротких операций, где счет идет на миллисекунды, но не для тех где счет может идти на секунды и минуты;
|
||
+ Сохранение порядка в параллельных стримах увеличивает издержки при выполнении и если порядок не важен, то имеется возможность отключить его сохранение и тем самым увеличить производительность, использовав промежуточную операцию `unordered()`:
|
||
|
||
```java
|
||
collection.parallelStream()
|
||
.sorted()
|
||
.unordered()
|
||
.collect(Collectors.toList());
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Какие конечные методы работы со стримами вы знаете?
|
||
+ `findFirst()` возвращает первый элемент;
|
||
+ `findAny()` возвращает любой подходящий элемент;
|
||
+ `collect()` представление результатов в виде коллекций и других структур данных;
|
||
+ `count()` возвращает количество элементов;
|
||
+ `anyMatch()` возвращает `true`, если условие выполняется хотя бы для одного элемента;
|
||
+ `noneMatch()` возвращает `true`, если условие не выполняется ни для одного элемента;
|
||
+ `allMatch()` возвращает `true`, если условие выполняется для всех элементов;
|
||
+ `min()` возвращает минимальный элемент, используя в качестве условия `Comparator`;
|
||
+ `max()` возвращает максимальный элемент, используя в качестве условия `Comparator`;
|
||
+ `forEach()` применяет функцию к каждому объекту (порядок при параллельном выполнении не гарантируется);
|
||
+ `forEachOrdered()` применяет функцию к каждому объекту с сохранением порядка элементов;
|
||
+ `toArray()` возвращает массив значений;
|
||
+ `reduce()`позволяет выполнять агрегатные функции и возвращать один результат.
|
||
|
||
Для числовых стримов дополнительно доступны:
|
||
|
||
+ `sum()` возвращает сумму всех чисел;
|
||
+ `average()` возвращает среднее арифметическое всех чисел.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Какие промежуточные методы работы со стримами вы знаете?
|
||
+ `filter()` отфильтровывает записи, возвращая только записи, соответствующие условию;
|
||
+ `skip()` позволяет пропустить определённое количество элементов в начале;
|
||
+ `distinct()` возвращает стрим без дубликатов (для метода `equals()`);
|
||
+ `map()` преобразует каждый элемент;
|
||
+ `peek()` возвращает тот же стрим, применяя к каждому элементу функцию;
|
||
+ `limit()` позволяет ограничить выборку определенным количеством первых элементов;
|
||
+ `sorted()` позволяет сортировать значения либо в натуральном порядке, либо задавая `Comparator`;
|
||
+ `mapToInt()`, `mapToDouble()`, `mapToLong()` - аналоги `map()` возвращающие стрим числовых примитивов;
|
||
+ `flatMap()`, `flatMapToInt()`, `flatMapToDouble()`, `flatMapToLong()` - похожи на `map()`, но могут создавать из одного элемента несколько.
|
||
|
||
Для числовых стримов дополнительно доступен метод `mapToObj()`, который преобразует числовой стрим обратно в объектный.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как вывести на экран 10 случайных чисел, используя `forEach()`?
|
||
```java
|
||
(new Random())
|
||
.ints()
|
||
.limit(10)
|
||
.forEach(System.out::println);
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как можно вывести на экран уникальные квадраты чисел используя метод `map()`?
|
||
```java
|
||
Stream
|
||
.of(1, 2, 3, 2, 1)
|
||
.map(s -> s * s)
|
||
.distinct()
|
||
.forEach(System.out::println);
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как вывести на экран количество пустых строк с помощью метода `filter()`?
|
||
```java
|
||
System.out.println(
|
||
Stream
|
||
.of("Hello", "", ", ", "world", "!")
|
||
.filter(String::isEmpty)
|
||
.count());
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как вывести на экран 10 случайных чисел в порядке возрастания?
|
||
```java
|
||
(new Random())
|
||
.ints()
|
||
.limit(10)
|
||
.sorted()
|
||
.forEach(System.out::println);
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как найти максимальное число в наборе?
|
||
```java
|
||
Stream
|
||
.of(5, 3, 4, 55, 2)
|
||
.mapToInt(a -> a)
|
||
.max()
|
||
.getAsInt(); //55
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как найти минимальное число в наборе?
|
||
```java
|
||
Stream
|
||
.of(5, 3, 4, 55, 2)
|
||
.mapToInt(a -> a)
|
||
.min()
|
||
.getAsInt(); //2
|
||
```
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как получить сумму всех чисел в наборе?
|
||
```java
|
||
Stream
|
||
.of(5, 3, 4, 55, 2)
|
||
.mapToInt()
|
||
.sum(); //69
|
||
```
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как получить среднее значение всех чисел?
|
||
```java
|
||
Stream
|
||
.of(5, 3, 4, 55, 2)
|
||
.mapToInt(a -> a)
|
||
.average()
|
||
.getAsDouble(); //13.8
|
||
```
|
||
[к оглавлению](#java-8)
|
||
|
||
## Какие дополнительные методы для работы с ассоциативными массивами (maps) появились в Java 8?
|
||
+ `putIfAbsent()` добавляет пару «ключ-значение», только если ключ отсутствовал:
|
||
|
||
`map.putIfAbsent("a", "Aa");`
|
||
|
||
+ `forEach()` принимает функцию, которая производит операцию над каждым элементом:
|
||
|
||
`map.forEach((k, v) -> System.out.println(v));`
|
||
|
||
+ `compute()` создаёт или обновляет текущее значение на полученное в результате вычисления (возможно использовать ключ и текущее значение):
|
||
|
||
`map.compute("a", (k, v) -> String.valueOf(k).concat(v)); //["a", "aAa"]`
|
||
|
||
+ `computeIfPresent()` если ключ существует, обновляет текущее значение на полученное в результате вычисления (возможно использовать ключ и текущее значение):
|
||
|
||
`map.computeIfPresent("a", (k, v) -> k.concat(v));`
|
||
|
||
+ `computeIfAbsent()` если ключ отсутствует, создаёт его со значением, которое вычисляется (возможно использовать ключ):
|
||
|
||
`map.computeIfAbsent("a", k -> "A".concat(k)); //["a","Aa"]`
|
||
|
||
+ `getOrDefault()` в случае отсутствия ключа, возвращает переданное значение по-умолчанию:
|
||
|
||
`map.getOrDefault("a", "not found");`
|
||
|
||
+ `merge()` принимает ключ, значение и функцию, которая объединяет передаваемое и текущее значения. Если под заданным ключем значение отсутствует, то записывает туда передаваемое значение.
|
||
|
||
`map.merge("a", "z", (value, newValue) -> value.concat(newValue)); //["a","Aaz"]`
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `LocalDateTime`?
|
||
`LocalDateTime` объединяет вместе `LocaleDate` и `LocalTime`, содержит дату и время в календарной системе ISO-8601 без привязки к часовому поясу. Время хранится с точностью до наносекунды. Содержит множество удобных методов, таких как plusMinutes, plusHours, isAfter, toSecondOfDay и т.д.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `ZonedDateTime`?
|
||
`java.time.ZonedDateTime` — аналог `java.util.Calendar`, класс с самым полным объемом информации о временном контексте в календарной системе ISO-8601. Включает временную зону, поэтому все операции с временными сдвигами этот класс проводит с её учётом.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как получить текущую дату с использованием Date Time API из Java 8?
|
||
```java
|
||
LocalDate.now();
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как добавить 1 неделю, 1 месяц, 1 год, 10 лет к текущей дате с использованием Date Time API?
|
||
```java
|
||
LocalDate.now().plusWeeks(1);
|
||
LocalDate.now().plusMonths(1);
|
||
LocalDate.now().plusYears(1);
|
||
LocalDate.now().plus(1, ChronoUnit.DECADES);
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как получить следующий вторник используя Date Time API?
|
||
```java
|
||
LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как получить вторую субботу текущего месяца используя Date Time API?
|
||
```java
|
||
LocalDate
|
||
.of(LocalDate.now().getYear(), LocalDate.now().getMonth(), 1)
|
||
.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY))
|
||
.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как получить текущее время с точностью до миллисекунд используя Date Time API?
|
||
```java
|
||
new Date().toInstant();
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как получить текущее время по местному времени с точностью до миллисекунд используя Date Time API?
|
||
```java
|
||
LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как определить повторяемую аннотацию?
|
||
Чтобы определить повторяемую аннотацию, необходимо создать аннотацию-контейнер для списка повторяемых аннотаций и обозначить повторяемую мета-аннотацией `@Repeatable`:
|
||
|
||
```java
|
||
@interface Schedulers
|
||
{
|
||
Scheduler[] value();
|
||
}
|
||
|
||
@Repeatable(Schedulers.class)
|
||
@interface Scheduler
|
||
{
|
||
String birthday() default "Jan 8 1935";
|
||
}
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `Nashorn`?
|
||
__Nashorn__ - это движок JavaScript, разрабатываемый на Java компанией Oracle. Призван дать возможность встраивать код JavaScript в приложения Java. В сравнении с _Rhino_, который поддерживается Mozilla Foundation, Nashorn обеспечивает от 2 до 10 раз более высокую производительность, так как он компилирует код и передает байт-код виртуальной машине Java непосредственно в памяти. Nashorn умеет компилировать код JavaScript и генерировать классы Java, которые загружаются специальным загрузчиком. Так же возможен вызов кода Java прямо из JavaScript.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Что такое `jjs`?
|
||
`jjs` это утилита командной строки, которая позволяет исполнять программы на языке JavaScript прямо в консоли.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Какой класс появился в Java 8 для кодирования/декодирования данных?
|
||
`Base64` - потокобезопасный класс, который реализует кодировщик и декодировщик данных, используя схему кодирования base64 согласно _RFC 4648_ и _RFC 2045_.
|
||
|
||
Base64 содержит 6 основных методов:
|
||
|
||
`getEncoder()`/`getDecoder()` - возвращает кодировщик/декодировщик base64, соответствующий стандарту _RFC 4648_;
|
||
`getUrlEncoder()`/`getUrlDecoder()` - возвращает URL-safe кодировщик/декодировщик base64, соответствующий стандарту _RFC 4648_;
|
||
`getMimeEncoder()`/`getMimeDecoder()` - возвращает MIME кодировщик/декодировщик, соответствующий стандарту _RFC 2045_.
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
## Как создать Base64 кодировщик и декодировщик?
|
||
```java
|
||
// Encode
|
||
String b64 = Base64.getEncoder().encodeToString("input".getBytes("utf-8")); //aW5wdXQ==
|
||
// Decode
|
||
new String(Base64.getDecoder().decode("aW5wdXQ=="), "utf-8"); //input
|
||
```
|
||
|
||
[к оглавлению](#java-8)
|
||
|
||
# Источники
|
||
+ [Хабрахабр - Новое в Java 8](https://habrahabr.ru/post/216431/)
|
||
+ [Хабрахабр - Шпаргалка Java программиста 4. Java Stream API](https://habrahabr.ru/company/luxoft/blog/270383/)
|
||
+ [METANIT.COM](http://metanit.com/java/tutorial/9.1.php)
|
||
+ [javadevblog.com](http://javadevblog.com/interfejsy-v-java-8-staticheskie-metody-metody-po-umolchaniyu-funktsional-ny-e-interfejsy.html)
|
||
|
||
[Вопросы для собеседования](README.md)
|