В этой статье мы рассмотрим новую функцию Java 12 «Выражения переключателя» и то, как ее можно использовать в сочетании с
Операция Stream::map и некоторые другие операции Stream. Узнайте, как вы можете улучшить свой код с помощью потоков и выражений переключателей.
Переключатель выражений
Java 12 поставляется с поддержкой «предварительного просмотра» «Switch Expressions». Выражение Switch позволяет операторам switch возвращать значения напрямую, как показано ниже:
|
1
2
3
4
5
6
7
|
public String newSwitch(int day) { return switch (day) { case 2, 3, 4, 5, 6 -> "weekday"; case 7, 1 -> "weekend"; default -> "invalid"; } + " category";} |
Вызов этого метода с 1 вернет «категорию выходного дня».
Это здорово и делает наш код короче и лаконичнее. Нам не нужно беспокоиться о возможных проблемах, блоках, изменяемых временных переменных или пропущенных случаях / дефолтах, которые могут иметь место для переключателя good ole. Просто посмотрите на этот соответствующий старый пример коммутатора, и вы поймете, что я имею в виду:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public String oldSwitch(int day) { final String attr; switch (day) { case 2,3,4,5,6: { attr = "weekday"; break; } case 7, 1: { attr = "weekend"; break; } default: { attr = "invalid"; } } return attr + " category";} |
Переключатель выражений является функцией предварительного просмотра
Чтобы заставить Switch Expression работать под Java 12, мы должны передать
“--enable-preview” как аргумент командной строки, когда мы компилируем и запускаем наше приложение. Это оказалось немного сложнее, но, надеюсь, станет легче с выпуском новых версий IDE и / или / если Java включит эту функцию в качестве полностью поддерживаемой функции. Пользователи IntelliJ должны использовать версию 2019.1 или новее.
Переключение выражений в Stream :: map
Выражения-переключатели очень просты в использовании в операторах Stream::map , особенно по сравнению со старым синтаксисом-переключателем. В приведенных ниже примерах я использовал ORM Speedment Stream и примерную базу данных Sakila . База данных Sakila — это все о фильмах, актерах и так далее.
Вот поток, который декодирует идентификатор языка фильма ( short ) в полное имя языка ( String ), используя map() в сочетании с выражением Switch:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
public static void main(String... argv) { try (Speedment app = new SakilaApplicationBuilder() .withPassword("enter-your-db-password-here") .build()) { FilmManager films = app.getOrThrow(FilmManager.class); List<String> languages = films.stream() .map(f -> "the " + switch (f.getLanguageId()) { case 1 -> "English"; case 2 -> "French"; case 3 -> "German"; default -> "Unknown"; } + " language") .collect(toList()); System.out.println(languages); }} |
Это создаст поток из всех 1000 фильмов в базе данных, а затем сопоставит каждый фильм с именем соответствующего языка и соберет все эти имена в список. Выполнение этого примера приведет к следующему выводу (сокращенно для краткости):
[английский язык, английский язык,…]
Если бы мы использовали старый синтаксис переключателя, мы бы получили что-то вроде этого:
|
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
|
... List<String> languages = films.stream() .map(f -> { final String language; switch (f.getLanguageId()) { case 1: { language = "English"; break; } case 2: { language = "French"; break; } case 3: { language = "German"; break; } default: { language = "Unknown"; } } return "the " + language + " language"; }) .collect(toList()); ... |
Или, может быть, что-то вроде этого:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
... List<String> languages = films.stream() .map(f -> { switch (f.getLanguageId()) { case 1: return "the English language"; case 2: return "the French language"; case 3: return "the German language"; default: return "the Unknown language"; } }) .collect(toList()); ... |
Последний пример короче, но дублирует логику.
Переключение выражений в Stream :: mapToInt
В этом примере мы вычислим сводную статистику о баллах, которые мы присваиваем на основе рейтинга фильма. Чем более ограничены, тем выше оценка в соответствии с нашей собственной изобретенной шкалой:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
IntSummaryStatistics statistics = films.stream() .mapToInt(f -> switch (f.getRating().orElse("Unrated")) { case "G", "PG" -> 0; case "PG-13" -> 1; case "R" -> 2; case "NC-17" -> 5; case "Unrated" -> 10; default -> 0; }) .summaryStatistics(); System.out.println(statistics); |
Это даст следующий результат:
|
1
|
IntSummaryStatistics{count=1000, sum=1663, min=0, average=1.663000, max=5} |
В этом случае разница между выражениями коммутатора и старым коммутатором не так уж велика. Используя старый ключ, мы могли бы написать:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
IntSummaryStatistics statistics = films.stream() .mapToInt(f -> { switch (f.getRating().orElse("Unrated")) { case "G": case "PG": return 0; case "PG-13": return 1; case "R": return 2; case "NC-17": return 5; case "Unrated": return 10; default: return 0; } }) .summaryStatistics(); |
Переключение выражений в Stream :: collect
В последнем примере показано использование выражения switch в группировке по Collector. В этом случае мы бы хотели посчитать, сколько фильмов может посмотреть человек определенного минимального возраста. Здесь мы используем карту с минимальным возрастом в качестве ключей и подсчитываем фильмы в качестве значений.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
Map<Integer, Long> ageMap = films.stream() .collect( groupingBy( f -> switch (f.getRating().orElse("Unrated")) { case "G", "PG" -> 0; case "PG-13" -> 13; case "R" -> 17; case "NC-17" -> 18; case "Unrated" -> 21; default -> 0; }, TreeMap::new, Collectors.counting() ) );System.out.println(ageMap); |
Это даст следующий результат:
|
1
|
{0=372, 13=223, 17=195, 18=210} |
Предоставляя (необязательно) поставщика TreeMap::new Map TreeMap::new , мы получаем возраст в отсортированном порядке. Почему PG-13 можно увидеть с 13 лет, а NC-17 — с 17, а с 18 лет — загадочно, но выходит за рамки этой статьи.
Резюме
Я с нетерпением жду, чтобы функция Switch Expressions была официально включена в Java. Выражения-переключатели иногда могут заменять лямбда-выражения и ссылки на методы для многих типов потоковых операций.
|
См. Оригинальную статью здесь: Java 12: Отображение с помощью выражений коммутатора. Мнения, высказанные участниками Java Code Geeks, являются их собственными. |