从 Spring PR 中学习代码技巧
D瓜哥经常关注 Spring 的 PR 与 Issue。在众多 Contributor 中,除了 Spring 团队成员之外,我对 stsypanov (Сергей Цыпанов) 印象很深刻。这哥们给 Spring 提了非常多的 PR,请看列表 Pull requests · spring-projects/spring-framework,而且这个哥们的 PR 都非常有特点,绝大部分是性能提升方面的 PR,而且还会给出 JMH 的测试结果。不愧是毛熊人,做事细致严谨。
这周心血来潮,把这哥们的 PR 翻一翻,希望可以学习一些编码技巧。简单记录一下,以备以后回顾学习。
提高 Map
的遍历性能
请看: SPR-17074 Replace iteration over Map::keySet with Map::entrySet by stsypanov · Pull Request #1891
摘取一个示例如下:
// --before update------------------------------------------------------
for (String attributeName : attributes.keySet()) {
Object value = attributes.get(attributeName);
// --after update-------------------------------------------------------
for (Map.Entry<String, Object> attributeEntry : attributes.entrySet()) {
String attributeName = attributeEntry.getKey();
Object value = attributeEntry.getValue();
这个改动很小,但是对性能的改善还是比较显著的。翻看自己项目的代码,还是有不少是改动前的写法。
针对这点,D瓜哥也给 Spring 发了一个 PR: Improve performance of iteration in GroovyBeanDefinitionReader by diguage · Pull Request #27100。相信不久就会合并到 main
分支的。
所以,给 Spring 以及其他开源项目提 PR,其实一点也不难。只要,你花心思去研究,肯定有机会的。不过,也反思一点:我这个 PR 有点东施效颦的感觉,有点刷 KPI 的样子。还是应该脚踏实地去好好研究,提更多更有建设性意见的 PR。
StringJoiner
看这个 PR: Use StringJoiner where possible to simplify String joining by stsypanov · Pull Request #22430 才知道,原来在 Java 8 直接内置了 StringJoiner
,翻看 StringJoiner
的源码,你会发现它出来可以设置连接符,竟然还可以设置前置符和后置符。后续也可以使用这个工具类。
ArrayList
初始化
从 PR 中摘录出两个修改片段:
// --before update-------------------------------------------
List<String> result = new ArrayList<>();
result.addAll(Arrays.asList(array1));
// --after update--------------------------------------------
List<String> result = new ArrayList<>(Arrays.asList(array1));
// --before update----------------------------------------
List<String> matchingHeaderNames = new ArrayList<>();
if (headers != null) {
for (String key : headers.keySet()) {
if (PatternMatchUtils.simpleMatch(pattern, key)) {
matchingHeaderNames.add(key);
}
// --after update-----------------------------------------
if (headers == null) {
return Collections.emptyList();
}
List<String> matchingHeaderNames = new ArrayList<>();
for (String key : headers.keySet()) {
if (PatternMatchUtils.simpleMatch(pattern, key)) {
matchingHeaderNames.add(key);
}
new ArrayList<>(Arrays.asList(array1))
与 Collections.emptyList()
都是一些值得关注的代码小技巧。另外,在第二个修改片段中,直接进行空值判断,还可以减少下面代码的括号嵌套层数。
字符串连接
Simplify String concatenation by stsypanov · Pull Request #22466 这个 PR 改动很小,代码也乏善可陈。但是,在这个 PR 的描述中,Contributor 给出了这个 PR 的解释,里面给出的 Reference: StringBuffer and StringBuilder performance with JMH ,详细对比了不同情况下,“字符串连接”的性能情况,读一读还是有不少收获的。这里直接把文章结论引入过来:
StringBuilder
is better thanStringBuffer
StringBuilder.append(a).append(b)
is better thanStringBuilder.append(a+b)
StringBuilder.append(a).append(b)
is better thanStringBuilder.append(a); StringBuilder.append(b);
StringBuilder.append()
and+
are only equivalent provided that they are not nested and you don’t need to pre-sizing the builderPre-sizing the
StringBuilder
is like pre-sizing anArrayList
; if you know the approximate size you can reduce the garbage by specifying a capacity up-front
数组填充
// --before update----------------------
for (int i = 0; i < bytes.length; i++) {
bytes[i] = 'h';
}
// --after update-----------------------
Arrays.fill(bytes, (byte) 'h');
用一行代码代替三行代码,何乐而不为呢?另外,估计很多人不知道 Arrays.fill(array, object);
这个 API。
Comparator
// --before update------------------------------------
Arrays.sort(ctors, (c1, c2) -> {
int c1pl = c1.getParameterCount();
int c2pl = c2.getParameterCount();
return (c1pl < c2pl ? -1 : (c1pl > c2pl ? 1 : 0));
});
// --after update-------------------------------------
Arrays.sort(ctors, (c1, c2) -> {
int c1pl = c1.getParameterCount();
int c2pl = c2.getParameterCount();
return Integer.compare(c1pl, c2pl);
});
Contributor 使用 Integer.compare(int, int)
来简化比较代码。所以,以后比较整数可以使用 Integer.compare(int, int)
。
其实,还可以更进一步:
// --before update----------------------------------------------------------
Arrays.sort(ctors, (c1, c2) -> {
int c1pl = c1.getParameterCount();
int c2pl = c2.getParameterCount();
return Integer.compare(c1pl, c2pl);
});
// --after update-----------------------------------------------------------
Arrays.sort(ctors, Comparator.comparingInt(Constructor::getParameterCount));
数组克隆
// --before update--------------------------------
String[] copy = new String[state.length];
System.arraycopy(state, 0, copy, 0, state.length);
return copy;
// --after update---------------------------------
return state.clone();
复制数组,以前只知 System.arraycopy
可以高效完成任务,以后可以使用 array.clone()
。