Интерфейс ExecutorService отделяет отправку задачи на выполнение от непосредственно самого выполнения. По сравнению с использованием потоков и интерфейсом Runnable удобно то, что с помощью
Сначала у вас может возникнуть мысль: «Ну зачем они опять меняют мой язык?» Но потом, после начала работы с новыми функциями, вы обнаружите, что благодаря им пишете более краткий и чистый код вдвое быстрее, чем раньше, — и осознаете, что уже не хотите возвращаться к «старому Java».
Преимущество секционирования в том, что сохраняются оба списка элементов потока данных, применение к которым секционирующей функции дает true или false. В предыдущем примере получить список невегетарианских блюд можно путем извлечения значений для ключа false ассоциативного массива partitionedMenu, вместо того чтобы задействовать две отдельные операции фильтрации: одну с вышеупомянутым предикатом, а вторую — с его отрицанием.
Двумя аргументами фабричного метода Collectors.groupingBy, с помощью которых в предыдущем разделе мы манипулировали элементами групп, можно воспользоваться и для группировки второго уровня. Для этого можно передать внешнему методу groupingBy второй внутренний groupingBy, задав в нем критерий второго уровня для классификации элементов потока
в классе Collectors есть перегруженный фабричный метод groupingBy, принимающий второй аргумент типа Collector наряду с обычной функцией классификации. Таким образом, появляется возможность перенести предикат фильтрации внутрь этого второго коллектора, вот так:
Map> caloricDishesByType =
menu.stream()
.collect(groupingBy(Dish::getType,
filtering(dish -> dish.getCalories() > 500, toList())));
filtering — еще один статический фабричный метод класса Collectors, принимающий в качестве параметров предикат для фильтрации элементов в каждой из групп и еще один коллектор для перегруппировки профильтрованных элементов.
При наследовании классом метода с одной сигнатурой из нескольких мест (других классов или интерфейсов) работают три правила.
1. У классов всегда преимущество. Объявление метода в классе или суперклассе имеет приоритет перед объявлением любого метода с реализацией по умолчанию.
2. Если предыдущее правило не позволяет разрешить неоднозначность, то преимущество — у производных интерфейсов: используется метод с той же сигнатурой из наиболее конкретного интерфейса из числа содержащих реализацию по умолчанию (если интерфейс B расширяет интерфейс A, то B — более конкретный, чем A).
3. Наконец, если неоднозначность сохраняется, то класс, наследующий метод из нескольких интерфейсов, должен явным образом выбрать используемую реализацию метода по умолчанию, переопределив его и явно вызвав нужный метод.
Мы обещаем, что вам нужно знать только эти правила, больше ничего!
С помощью Stream API можно выражать сложные запросы обработки данных. Распространенные потоковые операции перечислены в табл. 5.1.
• С помощью методов filter, distinct, takeWhile (Java 9), dropWhile (Java 9), skip и limit можно фильтровать потоки данных и получать их срезы.
• Методы takeWhile, dropWhile работают эффективнее, чем filter, в случае отсортированного источника данных.
• С помощью методов map и flatMap можно извлекать элементы потоков данных и преобразовывать их.
• Используя методы findFirst и findAny, можно находить элементы потока данных. С помощью методов allMatch, noneMatch и anyMatch можно сопоставлять элементы потока с заданным предикатом.
• Эти методы реализуют сокращенную схему вычислений: вычисление прекращается сразу по обнаружении искомого; обрабатывать весь поток данных не требуется.
• С помощью метода reduce можно последовательно группировать все элементы потока для получения единого результата, например для вычисления суммы или максимума значений элементов потока данных.
• Часть операций, например filter и map, не сохраняют состояние. А некоторые, например reduce, для вычисления значения сохраняют состояние. Отдельные операции, такие как sorted и distinct, также сохраняют состояние, поскольку им необходимо буферизовать все элементы потока, чтобы вернуть новый поток. Подобные операции называются операциями с сохранением состояния.
• Существует три специализированные версии потоков для простых типов данных: интерфейсы IntStream, DoubleStream и LongStream. Их операции также специализированы соответствующим образом.
• Потоки данных можно создавать на основе не только коллекций, но и значений, массивов, файлов и с помощью конкретных методов, например iterate и generate.
• У бесконечного потока данных — бесконечное количество элементов (например, все возможные строки). Это возможно благодаря тому, что элементы в подобных потоках генерируются по требованию. С помощью таких методов, как limit, можно получить из бесконечного потока данных конечный.
захватываемые локальные переменные должны быть явным образом объявлены как final или фактически являться таковыми. Лямбда-выражения могут захватывать лишь локальные переменные, значение которых задается однократно (примечание: захват переменной экземпляра класса можно рассматривать как захват локальной final-переменной this). Например, следующий код не скомпилируется, поскольку переменной portNumber значение присваивается дважды:
Возможно, вы недоумеваете: почему на локальные переменные накладываются такие ограничения? Во-первых, существует ключевое различие во внутренней реализации переменных экземпляра класса и локальных переменных. Переменные экземпляра класса (объекта) хранятся в куче, в то время как локальные переменные располагаются в стеке. Если бы лямбда могла обратиться к локальной переменной напрямую, причем использовалась в отдельном потоке выполнения, то этот поток мог бы попытаться обратиться к данной переменной после того, как поток, выделивший под нее память, освободит ресурсы. Поэтому доступ к свободной локальной переменной в Java реализован в виде доступа к ее копии, а не к исходной переменной. Если присвоение значения переменной выполняется лишь однократно, то никакой разницы нет — отсюда и возникает вышеописанное ограничение.
Во-вторых, это ограничение также препятствует реализации типичных паттернов императивного программирования (которые, как мы поясним в дальнейших главах, затрудняют распараллеливание), связанных с изменением значения внешней переменной.
github.com/java-manning/modern-java