JVM函数式编程
分享人:杨景
分享人:杨景
来自公司真实代码:ChapterServiceImpl#getChapterList
public Map<String, Object> getChapterList(String userId) {
Map<String, Object> result = new HashMap<>();
List childChapter = new ArrayList();
//获得孩子
List<PStudentParent> children = chapterMapper.findChildrenByParentId(userId);
for (PStudentParent child : children) {
//准备孩子的信息
Map<String,Object> childMap = getChildrenChapter(child);
childChapter.add(childMap);
}
result.put("childrenlist", childChapter);
return result;
}
命令式编程
public Map<String, Object> getChapterList2(String userId) {
List<PStudentParent> children = chapterMapper.findChildrenByParentId(userId);
List<Map<String, Object>> childChapter = children.stream().map(this::getChildrenChapter).collect(Collectors.toList());
return new HashMap<String, Object>(){{ put("childrenlist", childChapter); }};
}
函数式,基于表达式的编程
Non Java 8
public static DayStartAndEndEntity getDayTime() {
DayStartAndEndEntity dayStartAndEndEntity = new DayStartAndEndEntity();
long current = System.currentTimeMillis();
long zero = current/(1000*3600*24)*(1000*3600*24) - TimeZone.getDefault().getRawOffset();
dayStartAndEndEntity.setStartTime(zero);
long twelve=zero+24*60*60*1000-1;//今天23点59分59秒的毫秒数
dayStartAndEndEntity.setEndTime(twelve);
return dayStartAndEndEntity;
}
Use Java 8 time
public static DayStartAndEndEntity getDayTime() {
LocalDateTime nowBegin = LocalDateTime.of(LocalDate.now(), LocalTime.MIN); // 1
long startMillis = nowBegin.toInstant(ZoneOffset.ofHours(8)).toEpochMilli(); // 2
long endMillis = start + (60L * 60 * 24 * 1000 - 1); // 3
return new DayStartAndEndEntity(start, end);
}
public PageInfoBT commentMore(Page page, ActivityCommentMoreDTO activityCommentMoreDTO) {
List<ReplyComment> list = activityCommentMapper.commentMore(page, activityCommentMoreDTO);
list.stream().forEach(x -> {
String commentatorId = x.getCommentatorId();
String commenteeId = x.getCommenteeId();
String commentatorName = userService.selectNameByChildId(commentatorId);
String commenteeName = userService.selectNameByChildId(commenteeId);
x.setCommentatorName(commentatorName);
x.setCommenteeName(commenteeName);
});
return new PageInfoBT<>(page, list);
}
假设list.size
为10,则forEach
循环一共要执行20次SQL查询。
public PageInfoBT commentMore2(Page page, ActivityCommentMoreDTO activityCommentMoreDTO) {
List<ReplyComment> list = activityCommentMapper.commentMore(page, activityCommentMoreDTO);
List<String> userIds = list.stream()
.flatMap(item -> Stream.of(item.getCommentatorId(), item.getCommenteeId()))
.collect(Collectors.toList());
HashMap<String, String> userIdNameMap = userService.selectIdNameMapByUserIds(userIds);
list.forEach(x -> {
x.setCommentatorName(userIdNameMap.get(x.getCommentatorId()));
x.setCommenteeName(userIdNameMap.get(x.getCommenteeId()));
});
return new PageInfoBT<>(page, list);
}
`
通过提前收集所有用户ID,将20次SQL查询降低为1次。
一种紧凑的、传递行为的方式。
无参
Runnable noArguments = () -> System.out.println("Hello World");
有参
ActionListener oneArgument = event -> System.out.println("button clicked");
多行
Runnable multiStatement = () -> {
System.out.print("Hello");
System.out.println(" World");
};
自动推导类型
BinaryOperator<Long> add = (x, y) -> x + y;
显示声明类型
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;
只能引用显示声明为final
的变量
final String name = getUserName();
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.out.println("hi " + name);
}
});
可引用既成事实的final
变量
String name = getUserName();
button.addActionListener(event -> System.out.println("hi " + name));
未成既成事实的final
变量无法编译通过
String name = getUserName();
name = formatUserName(name);
button.addActionListener(event -> System.out.println("hi " + name));
函数接口是只有一个抽象方法的接口,用途Lambda表达式的类型。
函数式接口可以做到向下兼容性,Java已经有了很多单抽象方法的接口,这些接口在不作任务修改的情况下可用于Java 8的Lambda表达式。
Java中重要的函数接口
接口 | 参数 | 返回类型 | 示例 |
---|---|---|---|
Predicate<T> |
T |
boolean | 是否匹配 |
Consumer<T> |
T |
void | 消费一个元素 |
Function<T, R> |
T |
R | 获得Persion对象的名字 |
Supplier<T> |
None |
T | 工厂方法,生成一个对象 |
UnaryOperator<T> |
T |
T | 逻辑非(! ) |
BinaryOperator<T> |
(T, T) |
T | 求两个数的乘积(* ) |
计算属于5.3班(5年级3班)的学生数量
int count = 0;
for (Student student : students) {
if (student.isClass("5.3") {
count += 1;
}
}
使用Java 8 Stream计算
long count = students.stream()
.filter(student -> student.isClass("5.3"))
.count();
collect(toList())
。求值动作,将Stream里的值生成一个列表。map
。映射转换,通过函数(Lambda)将Stream里的值转换成另外一种类型。filter
。过滤,只保留匹配为真的元素。flatMap
。用Stream替换值,然后将多个Stream连接成一个Stream。max
和min
。在Stream上求最大值和最小值。count
。计算Stream的数量。reduce
。从Stream中生成一个值,int count = Stream.of(1, 2, 3).reduce(0, (acc, element) -> acc + element);
assertEquals(6, count);
找到专辑里所有长度大于1分钟的曲目名称。
public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames = new HashSet<>();
for(Album album : albums) {
for (Track track : album.getTrackList()) {
if (track.getLength() > 60) {
String name = track.getName();
trackNames.add(name);
}
}
}
return trackNames;
}
重构 1 等价替换:for
-> forEach
public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames = new HashSet<>();
albums.stream()
.forEach(album -> {
album.getTracks()
.forEach(track -> {
if (track.getLength() > 60) {
String name = track.getName();
trackNames.add(name);
}
});
});
return trackNames;
}
重构 2 内层forEach
-> Stream
public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames = new HashSet<>();
albums.stream()
.forEach(album -> {
album.getTrackList().stream()
.filter(track -> track.getLength() > 60)
.map(track -> track.getName())
.forEach(name -> trackNames.add(name));
});
return trackNames;
}
重构 3 消除嵌套的Stream
,将Stream[Stream[Track]]
拉平为Stream[Track]
public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames = new HashSet<>();
albums.stream()
.flatMap(album -> album.getTrackList().stream())
.filter(track -> track.getLength() > 60)
.map(track -> track.getName())
.forEach(name -> trackNames.add(name));
return trackNames;
}
flatMap
将两层Stream
拉平为一层Stream
。
重构 4 消除临时变量,完整Stream
化
public Set<String> findLongTracks(List<Album> albums) {
return albums.stream()
.flatMap(album -> album.getTrackList().stream()) // 1
.filter(track -> track.getLength() > 60) // 2
.map(track -> track.getName()) // 3
.collect(toSet()); // 4
}
Stream
收集成Set
标准语法为Classname::methodName
,后面不需要加小括号。
artists.map(artist -> artist.getName())
可转简写为方法引用:
artists.map(Artist::getName)
(name, nationality) -> new Artist(name, nationality)
可简写为方法引用:
Artist::new
高阶函数指:接受另一个函数作为参数,或返回一个函数的函数。
Stream接口中几乎所有函数都是高阶函数。
定义一个异步函数
public CompletableFuture<Double> getPriceAsync(String product) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return random.nextDouble();
});
}
典型的“混聚”式应用
假定每个getTopic
和getHotTopic
函数都需要花费1秒钟执行。
public static void blockProcess() {
Instant begin = Instant.now();
String twitterTopic = getTopic("twitter");
String facebookTopic = getTopic("facebook");
String weiboTopic = getTopic("weibo");
List<String> topics = Arrays.asList(twitterTopic, weiboTopic, facebookTopic);
String hotTopic = getHotTopic(topics);
Duration duration = Duration.between(begin, Instant.now());
System.out.println("执行时间:" + duration + "。" + hotTopic);
}
执行时间:PT4.001S。twitter
public static void asyncProcess() throws ExecutionException, InterruptedException {
Instant begin = Instant.now();
CompletableFuture<String> twitterFuture = getTopicAsync("twitter");
CompletableFuture<String> facebookFuture = getTopicAsync("facebook");
CompletableFuture<String> weiboFuture = getTopicAsync("weibo");
CompletableFuture<List<String>> listFuture = sequence(Arrays.asList(twitterFuture, facebookFuture, weiboFuture));
CompletableFuture<String> hotTopic = listFuture.thenCompose(sortedList -> topTopicAsync(sortedList));
String frenchTopic = hotTopic.get();
Duration duration = Duration.between(begin, Instant.now());
System.out.println("执行时间:" + duration + "。" + frenchTopic);
}
执行时间:PT2.072S。twitter
一个异步操作完成后再执行另一个异步操作,但整体非阻塞:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> result1 = future1.thenCompose(n -> CompletableFuture.supplyAsync(() -> n + 3));
同时执行两个异步操作,以非阻塞的方式将结果合并到一起:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 2);
CompletableFuture<Integer> result2 = future1.thenCombine(future2, (x, y) -> x + y);
List<CompletableFuture<T>>
以非阻塞的方式将每个异步操作结果聚合到一起,返回CompletableFuture<List<T>>
:
public static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futures) {
CompletableFuture<Void> allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
return allDoneFuture.thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
}
从3个异步操作里取最先返回那个的结果,其它忽略:
CompletableFuture<Object> result3 = CompletableFuture.anyOf(future1, future2, future3);
immutable数据类型
scala> val a = 1
a: Int = 1
scala> a = 2
<console>:12: error: reassignment to val
a = 2
^
scala> val name = "杨景"
name: String = 杨景
scala> val s1 = s"中少红卡(北京)教育科技有限公司码农:$name"
s1: String = 中少红卡(北京)教育科技有限公司码农:杨景
scala> val s2 = s"当前时间:${LocalDateTime.now()}"
s2: String = 当前时间:2019-01-16T23:58:07.064
所有语句都是表达式
if表达式有值
scala> val b = if (true) "真" else "假"
b: String = 真
for comprehension(for推导值)有值
scala> val oddX2 = for (item <- list if item % 2 == 1) yield item * 2
oddX2: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 6, 10, 14, 18)
Java
public void receive(message: Object) {
if (message isInstanceOf String) {
String strMsg = (String) message;
....
} else if (message isInstanceOf java.util.Date) {
java.util.Date dateMsg = (java.util.Date) message;
....
} ....
}
Scala
def receive(message: Object): Unit = {
message match {
case str: String => println("message is String")
case date: java.util.Date => println("message is Date")
case _ => println("message is other type")
}
}
scala> def calc(n1: Int, n2: Int): (Int, Int) = {
| (n1 + n2, n1 * n2)
| }
calc: (n1: Int, n2: Int)(Int, Int)
scala> val (add, sub) = calc(5, 1)
add: Int = 6
sub: Int = 5
scala> val calcVar = calc _
calcVar: (Int, Int) => (Int, Int) = <function2>
scala> calcVar(2, 3)
res4: (Int, Int) = (5,6)
scala> val sum: (Int, Int) => Int = (x, y) => x + y
sum: (Int, Int) => Int = <function2>
scala> sum(5, 7)
res5: Int = 12
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
val futures = (0 until 10).map { i =>
Future {
val s = i.toString
print(s)
s
}
}
val future = Future.reduce(futures)((x, y) => x + y)
val result = Await.result(future, Duration.Inf) // result is 0123456789
def strtr(src:String, from:String, to:String): String
from 和 to是等长的字符串, 要求将src中值为from(i)的字符转换成to(i)
例如: strtr("abcdaf", "ac", "AC") == "AbCdAf"
public static String strtr(String src, String from, String to) {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < src.length(); i++) {
char c = src.charAt(i);
char cc = c;
for (int j = 0; j < from.length(); j++) {
if (from.charAt(j) == c) {
cc = to.charAt(j);
break;
}
}
buffer.append(cc);
}
return buffer.toString();
}
def strtr(src: String, from: String, to: String): String = {
val mat = (from zip to).toMap
src.map(s => mat.getOrElse(s, s))
}
杨景
www.yangbajing.me
weibo.com/yangbajing
yangbajing at gmail com