Рефакторинг для потоков Java 8

  1. Сравнение кодов
  2. Java 7
  3. Глубокое погружение в Java 8!
  4. Работа с потоком.
  5. Список участников
  6. Следующие шаги
  7. Ресурсы:

Изображение предоставлено toptal

Изображение предоставлено toptal.com

Как Java-разработчик, хорошо знакомый с API коллекций Java, я был рад узнать об API-интерфейсах Lambdas и Streams, которые являются частью выпуска Java 8. Было обещано увеличение производительности при обработке коллекций и упрощенный синтаксис кодирования, и, похоже, команда Java 8 выполнила эту задачу. Этот выпуск предоставляет совершенно новый способ обработки коллекций данных, а также новые и более эффективные шаблоны и синтаксис кодирования. Кроме того, API использует преимущества параллельных архитектур прямо из коробки!

Когда вы впервые узнаете о Lambdas, Streams и новом синтаксисе, вы можете подумать, что примеры и учебники, найденные в Интернете, немного тривиальны, но на самом деле это хорошо. Понятия этих концепций могут занять некоторое время. Все они взаимосвязаны и основаны на новых типах и интерфейсах, которые были представлены в Java 8 в пакете java.util.function. Концепция функциональных интерфейсов имеет решающее значение для понимания Lambdas, и Lambdas необходимо понимать, если вы хотите понимать потоки. Вы должны немного углубиться в фон функциональных интерфейсов и Lambdas, чтобы понять, почему они имеют смысл, а затем вы должны понять - и привыкнуть - некоторые совершенно новые обозначения для определения Lambdas. С этого момента это еще один интеллектуальный шаг к их практическому использованию с новыми методами, предоставляемыми для обработки потоков.

Как профессиональный разработчик, я очень заинтересован в лучшем пути для будущего развития, особенно учитывая повышение производительности, которое приносит Java 8. Но с таким большим изменением в языке, связанном с обработкой коллекций, становится ясно, что могут быть многочисленные возможности для рефакторинга существующего кода коллекций для большей эффективности. В этом посте я собираюсь пройтись по преобразованию небольшого фрагмента кода Java 7 в код Java 8 с использованием Lambdas и Streams, как для демонстрации некоторых концепций, так и для обеспечения отправной точки для собственного исследования.

Это не учебник по функциональным интерфейсам, лямбдам или потокам - в Интернете есть множество ресурсов по этим темам (для получения дополнительной информации см. Ссылки в конце этого поста). Эта статья посвящена мыслительному процессу замещения старого способа перебора коллекции новым способом Java 8. Для читателя меня меньше интересуют лучшие и наиболее эффективные нотации для кода, чем концепции и понимание новых методов, а также способы их использования в простом примере рефакторинга.

Сравнение кодов

Давайте рассмотрим пример кода, написанного для API коллекций Java 7. Этот код предназначен для получения отсортированного списка профилей клиентов из списка активных кампаний с использованием списка участников этих кампаний. Профили клиентов будут собираться из каждой Кампании путем итерации по Участникам Кампании и возврата всем Участникам с желаемым кодом кампании «1».

Java 7

Во-первых, мы должны заполнить наш новый список профилей клиентов в соответствии с нашими требованиями:

List customerProfiles = new ArrayList (); for (Кампания c: кампании) {if (c.isActive ()) {for (Участник p: c.getParticipants ()) {if (p.getCampaignCode () == 1) {customerProfiles.add (p.getCustomerProfile () ); }}}}

Далее нам нужен список CustomerProfiles, отсортированный по имени.

Collections.sort (customerProfiles, new Comparator () {public int Compare (CustomerProfile c1, CustomerProfile c2) {return c1.getName (). CompareTo (c2.getName ());}});

Обратите внимание, что во время сортировки мы изменяем объект customerProfiles.

Глубокое погружение в Java 8!

Итак, теперь давайте использовать методы и нотацию Java 8 для решения этой же проблемы кодирования.

Первый прыжок, который мы должны сделать, - это представить нашу коллекцию входных данных не просто как список объектов, но как список объектов, который поставляется со встроенным итератором. Он не только поставляется с итератором, но итератор уже «включен» и готов сделать элементы доступными с помощью одного вызова метода!

В Java 8 уже предполагается, что вы будете выполнять итерации по списку, поэтому вам будет легко это сделать с помощью метода stream ().

Этот оператор является новым способом Java 8 для перебора коллекции и предоставления объектов для обработки:

campaigns.stream ()

Этот вызов метода, который может быть вызван для любого объекта Коллекции, открывает поток, чтобы начать производить объекты в списке для вас, один за другим.

Обратите внимание, что цикл for больше не нужен! С Streams мы получаем это действие для цикла цикла автоматически.

Работа с потоком.

Поскольку поток сейчас активно возвращает нам объекты, нам лучше что-то с ними сделать.

У нас есть список кампаний, поэтому теперь мы должны взглянуть на логику, чтобы увидеть, какие другие виды операций нам нужно будет выполнить, чтобы получить наш результат. Глядя на наш инструментарий Java 8, мы видим, что он включает в себя следующие элементы:

filter () - выбрать записи на основе некоторых критериев map () - преобразовать объект в другой объект

Конечно, глядя на наш старый код, мы знаем, что у нас есть выбор. Мы хотим выбирать только активные кампании, и мы хотим выбирать только участников с кодом кампании 1.

Мы знаем, что фильтр () делает это, поэтому давайте использовать его, чтобы указать выборки. Мы транслируем тип «Кампания», поэтому мы будем использовать переменную метку-заполнитель слева от стрелки лямбда-нотации.

Кроме того, мы выбираем только активные кампании, поэтому критерии выбора будут идти справа и возвращать логическое значение. Метод filter (), конечно, будет фильтровать данные на основе результатов логического выражения.

Используя лямбда-нотацию, это довольно просто:

фильтр (кампания -> campaign.isActive ())

Глядя на старый код, мы видим, что нам также нужно перебрать и отфильтровать список типа «Участник». Это также достаточно просто написать:

фильтр (участник -> участник.getCampaignCode () == 1))

Но подождите секунду! У нас пока нет потока участников для фильтрации. Сейчас мы транслируем Кампании. Как мы должны попасть в список участников?

Список участников

Вот где приходит map (). Вы должны посмотреть на метод map () как способ сделать что-то на основе чего-то, что дано, а затем вернуть что-то еще.

В случае функциональных интерфейсов, таких как map (), поведение преднамеренно определяется таким общим и абстрактным образом, чтобы его можно было применять ко всем видам задач.

В нашем случае мы хотим дать карте () одну кампанию, и мы хотим, чтобы карта вернула нам список участников на основе этой кампании.

Итак, мы по существу преобразуем наш поток объектов Campaign, один за другим, в другой поток объектов-участников, один список для каждой кампании:

карта (кампания -> campaign.getParticipants ())

Итак, наш встроенный код теперь выглядит так:

campaigns.stream () .filter (campaign -> campaign.isActive ()) .map (кампания -> campaign.getParticipants ()) .filter (участник -> member.getCampaignCode () == 1))
### Профили клиентов

Теперь мы должны получить профиль клиента для каждого участника. Это будет карта или фильтр? Поскольку мы даем Участнику и получаем Профиль Клиента для этого Участника, то это будет
еще одна операция на карте.

карта (участник -> member.getCustomerProfile ())

Теперь мы транслируем (и работаем с) Профили клиентов! Мы начали с Кампании и, наконец, углубились в поток профилей клиентов, которые нам нужны. Итак, давайте отсортируем их по имени профиля клиента, используя стандартную лямбда-нотацию для отсортированного метода.

отсортировано (Comparator.comparing (customerProfile -> customerProfile.getName ()))
### Список возврата

Последний шаг - поместить их в список возврата из вывода нашего сортировщика. Помните, что мы все еще используем потоковые методы, поэтому имеем дело с объектами, готовыми к обработке. В конце поток должен быть помещен в структуру данных для следующего бита обработки.

Стандартный способ сбора результатов в список - это утверждение:

сбор (Collectors.toList ())

Сложив эти вещи вместе, мы получим это как наш первый переработанный код.

Список customerProfiles = campaigns.stream () .filter (кампания -> campaign.isActive ()) .map (кампания -> campaign.getParticipants ()) .filter (участник -> member.getCampaignCode () == 1) .map ( участник -> member.getCustomerProfile ()) .sorted (Comparator.comparing (customerProfile -> customerProfile.getName ())) .collect (Collectors.toList ())

Обратите внимание, что кодировка Java8 гораздо более краткая, а операции выбора и отображения показаны явно и просты для понимания. Также обратите внимание, что мы не мутировали исходный объект, и мы можем легко подключить новую обработку в любой точке цепочки методов Stream.

Следующие шаги

Хотя приведенный выше код не является единственным или даже «лучшим» способом решения этой проблемы с использованием средств Java 8, он является хорошей отправной точкой для того, чтобы увидеть, как концепции объединяются в простом примере рефакторинга.

Я призываю читателя ознакомиться с тем, как приведенные выше выражения Lambda можно еще раз подвергнуть рефакторингу с более краткой записью, а также прочитать и понять различные варианты метода collect ().

Я надеюсь, что этот пример поможет вам понять первые шаги и мыслительные процессы, стоящие за рефакторингом некоторого вашего существующего кода для обработки в качестве потока в Java 8. Как всегда, ключ состоит в том, чтобы разбить проблему на несколько отдельных шагов, помня модель и структуры данных. Как только вы получите представление о том, какой шаг вам нужно сделать - сортировка, выбор, преобразование и т. Д. - вы обычно можете написать простое выражение Lambda и передать его в вызов метода Stream, чтобы выполнить задачу. Удачного кодирования!

Ресурсы:

Дальнейшее чтение по функциональным интерфейсам:
https://www.oreilly.com/learning/java-8-functional-interfaces

Для хорошего начала на Lambdas:
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html

Для учебника по потокам:
http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html

Как мы должны попасть в список участников?
Это будет карта или фильтр?