很多人都说list sort()排序比stream().sorted()排序性能更好,但是到底为什么呢。
本文章里面的所有测试案例都是基于:JDK8
先来一个简单版demo,看看效果
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Random rand = new Random();
for (int i = 0; i < 10000; i++) {
list.add(rand.nextInt(1000));
}
List<Integer> streamList = new ArrayList<>();
streamList.addAll(list);
Long streamStartTime = System.currentTimeMillis();
streamList.stream().sorted(Comparator.comparing(Integer::intValue)).collect(Collectors.toList());
System.out.println("stream.sort耗时:" + (System.currentTimeMillis() - streamStartTime) + "ms");
Long startTime = System.currentTimeMillis();
list.sort(Comparator.comparing(Integer::intValue));
System.out.println("list.sort()耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}
}
输出结果(万级):
stream.sort耗时:77ms
list.sort()耗时:6ms
输出结果(十万级):
stream.sort耗时:143ms
list.sort()耗时:76ms
输出结果(百万级):
stream.sort耗时:443ms
list.sort()耗时:269ms
再来一个复杂版本的demo
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
Random rand = new Random();
for (int i = 0; i < 10000000; i++) {
User user = new User();
user.setId("id" + i);
user.setSort(rand.nextInt(10000000));
userList.add(user);
}
List<User> streamUserList = new ArrayList<>();
streamUserList.addAll(userList);
Long streamStartTime = System.currentTimeMillis();
streamUserList.stream().sorted(Comparator.comparing(User::getSort)).collect(Collectors.toList());
System.out.println("stream.sort耗时:" + (System.currentTimeMillis() - streamStartTime) + "ms");
Long startTime = System.currentTimeMillis();
userList.sort(Comparator.comparing(User::getSort));
System.out.println("list.sort()耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}
}
class User {
private String id;
private Integer sort;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Integer getSort() {
return sort;
}
public void setSort(Integer sort) {
this.sort = sort;
}
}
输出结果(万级):
stream.sort耗时:48ms
list.sort()耗时:4ms
输出结果(十万级):
// 第一次
stream.sort耗时:100ms
list.sort()耗时:105ms
// 第二次
stream.sort耗时:115ms
list.sort()耗时:119ms
输出结果(百万级):
stream.sort耗时:627ms
list.sort()耗时:434ms
在一些情况下,List原生排序可能比使用流(stream)的排序更高效,这是由于以下几个原因:
- 避免了流的额外开销:使用流的排序需要创建一个流对象,并在排序操作上应用中间操作和终端操作。这涉及到创建和维护流的状态,以及对元素进行中间操作的处理。相比之下,原生排序直接对列表进行排序,无需创建额外的对象或执行中间操作。
- 优化的底层算法:原生排序通常使用高效的底层排序算法,例如快速排序(QuickSort)或归并排序(MergeSort)。这些算法在大多数情况下都具有较好的性能,并且经过了多年的优化和改进。相比之下,流的排序可能使用不同的排序算法,例如使用稳定排序算法,但其性能可能不如原生排序算法。
- 数据访问模式:原生排序直接在列表上进行排序,这样可以充分利用计算机内存的局部性原理。它可以有效地访问和操作连续的内存块,减少了缓存不命中和内存访问的开销。相比之下,流的排序可能需要频繁地访问和操作元素,这可能导致更多的缓存不命中和内存访问开销。
以上的比较是一般情况下的观察结果,并不是绝对的。
在某些情况下,使用流的排序可能与原生排序具有相近的性能,特别是当使用并行流进行排序时,可以利用多核处理器的并行计算能力。
此外,流的排序具有更好的可读性和可维护性,尤其在函数式编程风格的代码中。
因此,在选择使用哪种排序方式时,需要根据具体的应用场景和需求进行权衡和选择。
最好在自己的场景下模拟测试一下,写的多了;后面就更加有把握更加清晰