Популярный твит Марио Фуско впечатляюще показывает, в чем главное отличие императивного и функционального подходов к подобным алгоритмам:
Императив против функциональности — разделение интересов pic.twitter.com/G2cC6iBkDJ
— Марио Фуско (@mariofusco) 1 марта 2015 г.
Оба алгоритма делают одно и то же, вероятно, они одинаково быстры и разумны Тем не менее, один из алгоритмов гораздо проще писать и читать, чем другой. Разница заключается в том, что в императивном программировании различные алгоритмические требования распределены по всему блоку кода, тогда как в функциональном программировании каждое требование имеет свою собственную небольшую строку кода. Для сравнения:
- Зеленый: обработка ошибок
- Синий: критерии Стоп
- Красный: операции ввода-вывода
- Желтый: «Бизнес логика»
Функциональное программирование не всегда превосходит императивное программирование, как показано в других примерах в блоге jOOQ :
- Как использовать функциональное программирование Java 8 для генерации буквенной последовательности
- Как использовать потоки Java 8 для быстрой замены элементов в списке
Но вот пример из переполнения стека пользователем Aurora_Titanium , где разница столь же очевидна, как и в примере с Марио Фуско:
Вычисление повторяющихся значений в массиве
Идея состоит в том, чтобы вычислить сумму всех этих значений, которые дублируются в наборе значений. Например, следующий массив:
|
1
|
int[] list = new int[]{1,2,3,4,5,6,7,8,8,8,9,10}; |
… должен дать в результате что-то вроде:
|
1
|
Duplicate: 8. Sum of all duplicate values: 24 |
Императивный подход
Один из ответов пользователя Volkan Ozkan использует императивный подход и вычисляет сумму следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 10};int sum = 0;for (int j = 0; j < array.length; j++){ for (int k = j + 1; k < array.length; k++) { if (k != j && array[k] == array[j]) { sum = sum + array[k]; System.out.println( "Duplicate found: " + array[k] + " " + "Sum of the duplicate value is " + sum); } }} |
Подход работает только для отсортированных массивов, где дубликаты появляются сразу после друг друга. В этом случае, однако, это, вероятно, оптимальное решение с точки зрения производительности, если производительность действительно имеет значение для этого алгоритма .
Функциональный подход
Если для вас приемлемо небольшое снижение производительности (боксирование, сбор их в карты), и, возможно, это так, вы можете заменить вышеприведенный трудно читаемый код следующим фрагментом функциональной логики в стиле Java-8, которая гораздо яснее рассказывает, что делает:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 10};IntStream.of(array) .boxed() .collect(groupingBy(i -> i)) .entrySet() .stream() .filter(e -> e.getValue().size() > 1) .forEach(e -> { System.out.println( "Duplicates found for : " + e.getKey() + " their sum being : " + e.getValue() .stream() .collect(summingInt(i -> i))); }); |
или с объяснениями:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 10};// Create a Stream<Integer> from your dataIntStream.of(array) .boxed()// Group values into a Map<Integer, List<Integer>> .collect(groupingBy(i -> i))// Filter out those map values that have only // 1 element in their group .entrySet() .stream() .filter(e -> e.getValue().size() > 1)// Print the sum for the remaining groups .forEach(e -> { System.out.println( "Duplicates found for : " + e.getKey() + " their sum being : " + e.getValue() .stream() .collect(summingInt(i -> i))); }); |
(обратите внимание, что функциональный подход вычисляет суммы для каждого дублирующего значения, а не общую сумму, как императивный подход. Из первоначального вопроса это требование было не очень ясным)
Как мы уже говорили в предыдущей статье в нашем блоге, мощь функционального программирования с помощью API, такого как Java 8 Stream API, заключается в том, что мы приближаемся к выразительной силе декларативного программирования в стиле SQL. Мы больше не заботимся о запоминании индексов отдельных массивов и о том, как их вычислять и сохранять промежуточные результаты в некоторых буферах. Теперь мы можем сосредоточиться на действительно интересной логике, такой как: «что такое дубликат?» или «какая сумма меня интересует?»
Прочтите о том, как SQL сравнивается с потоками Java 8: общие предложения SQL и их эквиваленты в потоках Java 8
| Ссылка: | Сравнение императивных и функциональных алгоритмов в Java 8 от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и AND JOOQ . |