将整数列表分组到分区中

时间:2016-11-24 18:11:43

标签: java java-8 java-stream

在流中有一种简单的方法:

public static void main(String[] args) {
    List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    System.out.print(partitioningValues(integerList, 3));
}

private static Map<Integer, List<Integer>> partitioningValues(List<Integer> integerList, int numberOfPartitions) {

    Map<Integer, List<Integer>> integerListMap = new HashMap<>();
    BigDecimal limit = BigDecimal.valueOf(integerList.size() / (double) numberOfPartitions);
    int limitRounded = limit.setScale(0, BigDecimal.ROUND_UP).intValue();

    for (int i = 0; i < numberOfPartitions; i++) {

        int toIndex = ((i + 1) * limitRounded) > integerList.size() ? integerList.size() : (i + 1) * limitRounded;
        integerListMap.put(i, integerList.subList(i * limitRounded, toIndex));
    }

    return integerListMap;
}

结果:

  

{0 = [1,2,3,4],1 = [5,6,7,8],2 = [9,10]}

3 个答案:

答案 0 :(得分:1)

您可以使用groupingBy进行拆分。

如果需要按元素值

拆分流
int split = 4;
Map<Integer, List<Integer>> map2 = integerList.stream().collect(Collectors.groupingBy(i -> (i-1) / split));
System.out.println(map2);

如果需要按位置拆分流

int[] pos = { -1 };
Map<Integer, List<Integer>> map = integerList.stream().peek(e -> pos[0]++).collect(Collectors.groupingBy(e -> pos[0] / split));
System.out.println(map);

输出

{0=[1, 2, 3, 4], 1=[5, 6, 7, 8], 2=[9, 10]}

答案 1 :(得分:0)

我建议你使用这种方法:它从0迭代到numberOfPartitions,在每一步创建一个batchLength元素的子列表(只有最后一步可能少于{{1} }}}并收集batchLength中的子列表,其中键是当前步骤,值是当前步骤的子列表。

HashMap

按版本分组(非常类似于@Saravana的第二个解决方案):

public static Map<Integer, List<Integer>> partitioningValues(List<Integer> integerList, int numberOfPartitions) {
    int size = integerList.size();
    BigDecimal limit = BigDecimal.valueOf(size / (double) numberOfPartitions);
    int batchLength =  limit.setScale(0, BigDecimal.ROUND_UP).intValue();
    AtomicInteger step = new AtomicInteger();
    return IntStream.range(0, numberOfPartitions)
            .boxed()
              .collect(
                Collectors.toMap(
                   s -> step.getAndIncrement(), 
                   s -> integerList.subList(s * batchLength, Math.min((s+1)*batchLength, size)))
              );
}

答案 2 :(得分:0)

如果您不想改变共享变量以跟踪索引并希望保持流可比较,您仍然可以通过使用替代策略来实现此目的。

分区大小是单个分区中的最大整数数。在所有代码段中,让我们按如下方式定义partitionSize

int partitionSize = (list.size() - 1) / partitions + 1;

我们使用简洁的-1 / + 1表示法代替Math.ceil

一个简单的天真方法就是找到要分组的索引:

list.stream().collect(groupingBy(i -> list.indexOf(i) / partitionSize));

但是如果你关心性能,你想找到一种更好的方法来处理索引。

直观的方法可能是首先生成所有索引位置,然后迭代它们并收集子列表。这会给你这样的东西,结合List<List<Integer>>中的所有分区:

int[] indexes = IntStream.iterate(0, i -> i + partitionSize).limit(partitions+1).toArray();

IntStream.range(0, indexes.length - 1)
         .mapToObj(i -> list.subList(indexes[i], Math.min(indexes[i + 1], list.size())))
         .collect(toList());

如果我们接近列表的末尾,Math.min用于查找区间的正确结束边界。

但您可以将索引计算和循环结合起来,如下所示:

  IntStream.rangeClosed(0, list.size() / partitionSize)
           .mapToObj(i -> list.subList(i * partitionSize, Math.min((i+1) * partitionSize, list.size())))
           .collect(toList());

注意结果是List<List<Integer>>,其中每个列表索引都映射在分区的子列表中。

如果你真的想要一张带有按键0,1,2的地图,那么你可以收集到地图:

Map<Integer, List<List<Integer>>> result =
      IntStream.rangeClosed(0, list.size() / partitionSize)
               .mapToObj(i -> list.subList(i * partitionSize, Math.min((i + 1) * partitionSize, list.size())))
               .collect(Collectors.groupingBy(l -> l.get(0) / partitionSize));

或者,如果您不介意使用外部库,例如番石榴已经

Lists.partition(integerList, 3);
  

示例

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int partitions = 4;
int partitionSize = (list.size() - 1) / partitions + 1; //ceil

List<List<Integer>> result = IntStream.rangeClosed(0, list.size() / partitionSize)
                                      .mapToObj(i -> list.subList(i * partitionSize, Math.min((i+1) * partitionSize, list.size())))
                                      .collect(toList());

System.out.println(result);
     

结果:[[1,2,3],[4,5,6],[7,8,9],[10]]