前提
项目中用到了一个调度算法,会对List中的任务平均分组到不同的集群中,之前的思路是平均取余数,然后将余数加到最后一个分片中,这样资源有很大的浪费。因为最后的余数可以加到之前的所有的分片当中,而不必是最后一个。网上有一个流传度交广的算法是将余数分配到了前几个切片中,这样在调度比较频繁的时候,前几个人集群负载会较大,造成负载不均。现在的改进是将余数随机分配到不同的分片中。
源码
private static final Random JVM_RANDOM = new JVMRandom();
/**
* 将一个list均分成n个list,主要通过偏移量来实现的,改进版
* 对分片的算法作改进,如果是10个平分成4片,则A/B会分到3片,C/D分到2片,负载会更加均衡
* 现在改进后的remainder会随机加到平均后的分组list中
* @param source
* @return
*/
public static <T> List<List<T>> average(final List<T> source, final int n) {
if (source == null) {
throw new NullPointerException("List must not be null");
}
if (n <= 0) {
throw new IllegalArgumentException("Size must be greater than 0");
}
List<List<T>> result = new ArrayList<List<T>>();
int remaider = source.size() % n; // (先计算出余数)
int number = source.size() / n; // 然后是商
int offset = 0;// 偏移量
int[] idxs = Arrays.stream(buildNoRepeatIndex(n), 0, remaider).sorted().toArray();
for (int i = 0; i < n; i++) {
List<T> value = null;
if (remaider > 0 && containsInts(idxs, i)) {
value = source.subList(i * number + offset, (i + 1) * number + offset + 1);
remaider--;
offset++;
} else {
value = source.subList(i * number + offset, (i + 1) * number + offset);
}
result.add(value);
}
return result;
}
/**
* 创建不重复的数组下标
* @param source
* @return
*/
public static int[] buildNoRepeatIndex(int size) {
int values[] = new int[size];
int temp1, temp2, temp3;
for (int i = 0; i < size; i++) {
values[i] = i;
}
// 随机交换values.length次
for (int i = 0; i < size; i++) {
temp1 = JVM_RANDOM.nextInt(size); // 随机产生一个位置
temp2 = JVM_RANDOM.nextInt(size); // 随机产生另一个位置
if (temp1 != temp2) {
temp3 = values[temp1];
values[temp1] = values[temp2];
values[temp2] = temp3;
}
}
return values;
}
private static boolean containsInts(int[] array, int target) {
for (int value : array) {
if (value == target) {
return true;
}
}
return false;
}