千万不要小看代码细节的优化,有时候一个很小的优化就要你的代码执行效率数倍提升,如果这个优化点调用比较频繁,甚至有可能解决你整个系统的性能瓶颈。
官方文档上是这么说的,
描述可能没这么直观,来个例子你就明白了。
public class App { public static void main(String[] args) { String input = "input"; String result = Optional.ofNullable(input).orElse(defaultProcess()); System.out.println(result); } public static String defaultProcess() { System.out.println("defalut process"); return "default"; } }
运行结果:
defalut process input
然后你猜下下面这段代码的运行结果是啥,
public class App { public static void main(String[] args) { String input = "input"; String result = Optional.ofNullable(input).orElseGet(() -> defaultProcess()); System.out.println(result); } public static String defaultProcess() { System.out.println("defalut process"); return "default"; } }
结果是:
input
到这里你应该已经明白了, orElse
里的逻辑在任何时候都会执行,即使optional不为空。而 orElseGet
只在optional是空的时候才会执行。
可以想象,如果在实际项目中 defaultProcess
里的逻辑很耗时,使用后者对性能的提示还是很明显的。
比如把下面这种循环,
for (int i = 0; i < list.size(); i++) {...}
改成如下这种:
for (int i = 0, length = list.size(); i < length; i++) {...}
简单的size计算可能对性能影响不大,但是如果循环中的方法计算是类似从数据库count等耗时类的操作,有可能就成为系统的性能瓶颈。
如果我们能估计大概的内容长度,集合类的实例在创建时最好分配一个初始空间。可以明显的提升性能。这里拿 StringBuilder
举个例子,如果我们使用默认的构建器,会初始分配16字符空间,如下:
/** * Constructs a string builder with no characters in it and an * initial capacity of 16 characters. */ public StringBuilder() { super(16); }
append操作的时候,如果发现到达了最大容量,它会将自身容量增加到当前的2倍再加2,然后从旧的空间拷贝数据到新的空间。源码如下:
/** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); }
这是一个耗时的动作,而且有时候会浪费空间。试想一下,如果你知道业务场景是大概需要1000个字符。如果没有指定初始值, StringBuilder
在 append
过程中要多次分配空间,拷贝数据。而且在接近1000的时候如果再次分配也是直接翻倍的增加空间,就造成了空间的浪费。
这里说的是 stream
和 parallelStream
的区别。
parallelStream
并行流就是一个把内容分成多个数据块,并用不不同的线程分别处理每个数据块的流。最后合并每个数据块的计算结果。处理的线程数就是机器的处理器核心数。
从原理上讲,大部分场景下并行流处理是更快,来看个例子:
@Test public void streamCostTest(){ List<Integer> intList = mockData(); useStream(intList); useParallelStream(intList); } /** * 构造数据 * * @return */ public List<Integer> mockData() { List<Integer> intList = new ArrayList<Integer>(); for (int i = 0; i < 1000000; i++) { intList.add(i); } return intList; } public void useStream(List<Integer> integerList) { long start = System.currentTimeMillis(); long count = integerList.stream().filter(x -> (x%2==0)).count(); System.out.println(count); long end = System.currentTimeMillis(); System.out.println("useStream cost:" + (end - start)); } public void useParallelStream(List<Integer> integerList) { long start = System.currentTimeMillis(); long count = integerList.parallelStream().filter(x -> (x%2==0)).count(); System.out.println(count); long end = System.currentTimeMillis(); System.out.println("useParallelStream cost:" + (end - start)); }
测试结果:
500000 useStream cost:42 500000 useParallelStream cost:13
注意上面我提到了大部分场景下。也就是说并行流并不是大杀器,一劳永逸的解决方案。有些地方使用并行流反而性能更差,这里只给一个建议,就是一定要自己测试,测试性能,测试多线程安全等。
参考:
https://docs.oracle.com/javas...