零、背景
最近在做数据迁移
为了加速迁移速度
其中就需要把查询到的数据( max 100 条)
拆分成 5 份,然后执行 5 个子任务,加速处理
一、代码
1.1 常规做法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static <T> List<List<T>> split2(List<T> lists, int subCount) { if (CollectionUtils.isEmpty(lists) || subCount < 1) { return null; }
List<List<T>> result = new ArrayList<>();
int size = lists.size(); int count = (size + subCount - 1) / subCount;
for (int i = 0; i < count; i++) { List<T> subList = lists.subList(i * subCount, (Math.min((i + 1) * subCount, size))); result.add(subList); } return result; }
|
1.2 新颖做法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public static <T> List<List<T>> split(List<T> lists, int subCount) { if (CollectionUtils.isEmpty(lists) || subCount < 1) { return null; } int splitTimes = (lists.size() + subCount - 1) / subCount; return Stream .iterate(0, n -> n + subCount) .limit(splitTimes) .map(item -> lists.stream() .skip(item) .limit(subCount) .collect(Collectors.toList())) .collect(Collectors.toList()); }
|
三、性能
3.1 结果
从结果可以看出,一味地追求新颖也不是好事,需要知其然
java 8 的 stream 并不占优势,性能相差好几个数量级…
1 2 3
| Benchmark Mode Cnt Score Error Units ListSplit.split2Test thrpt 10 306050.155 ± 24646.689 ops/s ListSplit.splitTest thrpt 10 353.048 ± 122.423 ops/s
|
3.3 压测代码
压测教程:JMH 使用参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| import com.google.api.client.util.Lists; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.springframework.util.CollectionUtils;
import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream;
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 1)
@Measurement(iterations = 10) @BenchmarkMode(Mode.Throughput) public class ListSplit { private static final List<Integer> INTEGERS = Lists.newArrayList(); @Setup public void init() { List<Integer> list = Stream.iterate(0, n -> n + 1) .limit(1000) .collect(Collectors.toList()); INTEGERS.addAll(list); }
@Benchmark public void splitTest(){ List<List<Integer>> listList = split(INTEGERS, 3); } @Benchmark public void split2Test(){ List<List<Integer>> listList = split2(INTEGERS, 3); }
public static <T> List<List<T>> split(List<T> lists, int subCount) { int splitTimes = (lists.size() + subCount - 1) / subCount; return Stream .iterate(0, n -> n + subCount) .limit(splitTimes) .map(item -> lists.stream() .skip(item) .limit(subCount) .collect(Collectors.toList())) .collect(Collectors.toList()); }
public static <T> List<List<T>> split2(List<T> list, int len) { if (CollectionUtils.isEmpty(list) || len < 1) { return null; }
List<List<T>> result = new ArrayList<>();
int size = list.size(); int count = (size + len - 1) / len;
for (int i = 0; i < count; i++) { List<T> subList = list.subList(i * len, (Math.min((i + 1) * len, size))); result.add(subList); } return result; }
|
四、后话
其中不错的一点我觉得是
1
| int splitTimes = (lists.size() + subCount - 1) / subCount;
|
这样巧妙的计算,避免了先取模,后判断是否有余数的繁杂。
这个不是我想到的,也不知道怎么通过数学证明,但是这样就是对的 ==
ps:是谁说数学没用的 ????