在本文中,我们将研究Java 12的新功能“ Switch Expressions”,以及如何与Java 12结合使用。
Stream::map
操作和其他一些Stream操作。 了解如何使用流和开关表达式使代码更好。
切换表达式
Java 12附带了对“开关表达式”的“预览”支持。 Switch Expression允许switch语句直接返回值,如下所示:
public String newSwitch( int day) { return switch (day) { case 2 , 3 , 4 , 5 , 6 -> "weekday" ; case 7 , 1 -> "weekend" ; default -> "invalid" ; } + " category" ; }
用1
调用此方法将返回“周末类别”。
这很棒,并使我们的代码更短,更简洁。 我们不必费心去掉顾虑,块,易变的临时变量或遗漏的情况/默认情况,这可能是良好的开关的情况。 只要看一下对应的旧开关示例,您就会明白我的意思:
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
运算符中非常易于使用,尤其是与旧的开关语法相比时。 在以下示例中,我使用了Speedment Stream ORM和Sakila示例数据库 。 Sakila数据库包含有关电影,演员等的全部信息。
这是使用map()
结合Switch Expression将电影语言ID( short
)解码为完整语言名称( String
)的流:
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部电影的流,然后将每部电影映射到相应的语言名称,并将所有这些名称收集到一个列表中。 运行此示例将产生以下输出(为简洁起见,以下简称):
[英语,英语,…]
如果我们将使用旧的switch语法,则将获得如下所示的内容:
... 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()); ...
或者,也许是这样的:
... 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中切换表达式
在此示例中,我们将基于电影的评分来计算有关分配分数的摘要统计信息。 根据我们自己的发明规模,限制越严格,得分越高:
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);
这将产生以下输出:
IntSummaryStatistics{count= 1000 , sum= 1663 , min= 0 , average= 1.663000 , max= 5 }
在这种情况下,“开关表达式”与旧开关之间的差异并不大。 使用旧的开关,我们可以这样写:
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表达式的情况。 在这种情况下,我们要计算某个最低年龄的人可以看多少部电影。 在这里,我们使用年龄最小的地图作为键,将已计数的电影作为值。
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);
这将产生以下输出:
{ 0 = 372 , 13 = 223 , 17 = 195 , 18 = 210 }
通过提供(可选) groupingBy
地图供应商TreeMap::new
,我们可以按年龄顺序排列年龄。 为什么从13岁起就可以看到PG-13,而从17岁起就不能看到NC-17,而是从18岁起就看不到NC-17,但这不在本文的讨论范围之内。
摘要
我期待将Switch Expressions功能正式纳入Java。 开关表达式有时可以替换许多流操作类型的lambda和方法引用。
翻译自: https://www.javacodegeeks.com/2019/03/java-12-mapping-with-switch-expressions.html