繁体   English   中英

Java 8将n大小集合划分为m个未知大小的列表

[英]Java 8 divide n size collection to m lists of unknown size

一般问题我不知道如何将排序后的列表划分为较小的排序后的列表,但是不像Guava Lists.partition(list,size)那样进行Lists.partition(list,size) -因此,不能将指定大小的较小列表Lists.partition(list,size)为固定数量的列表大小相似。

例如,具有一个源列表:1,2,3,4我希望有3个列表作为结果(3个是结果列表的固定数量)。 我应该得到List<List<Long>>的结果:ListOne:1,ListTwo:2,ListThree:3,4(请记住要保留排序)。 当源列表小于目标列表的数量时,然后确定,我可以获得较小数量的列表。 因此,如果源列表为1,2,而我想拥有3个列表,则算法应返回两个列表:List1 1,List2:2。

源列表的大小是未知的,但是有成千上万的元素必须分成10个列表,因为那时有10个线程准备使用这些元素执行一些更复杂的操作。

下面的算法是完全错误的,在源列表上有14个元素,并通过GRD_SIZE=10它返回2个元素的7个列表。 它应该返回GRD_SIZE=10个相似大小的列表。 可能我也不应使用Guava Lists.partition方法...但是如何执行此任务?

List<List<Long>> partitions = partition(sourceList, GRD_SIZE);

public static <T> List<List<T>> partition(List<T> ascSortedItems, int size)
{
     int threadSize =  (int) Math.ceil(
         new BigDecimal(ascSortedItems.size()).divide(
             new BigDecimal(
                 ascSortedItems.size() >= size ? size : ascSortedItems.size()
             )
          ).doubleValue()
     );

     final AtomicInteger counter = new AtomicInteger(0);

     return ascSortedItems.stream()
         .collect(Collectors.groupingBy(it -> counter.getAndIncrement() / threadSize))
         .values()
         .stream()
         .collect(Collectors.toList());
}

首先,您不应该像尝试的解决方案那样使用可变计数器。 一个规范的替代方案将类似于以下答案 ,该方案在适应您的问题后将如下所示:

 return IntStream.range(0, (ascSortedItems.size()+threadSize-1)/threadSize) .mapToObj(i -> ascSortedItems .subList(i*threadSize, Math.min(ascSortedItems.size(), (i+1)*threadSize))) .collect(Collectors.toList()); } 

threadSize的计算不需要怪异的BigDecimal弯路。 您可以将其计算为

int threadSize = Math.max(1, ascSortedItems.size()/size);

四舍五入时

int threadSize = Math.max(1, (ascSortedItems.size()+size-1)/size);

四舍五入。

但两者都无法按预期方式工作。 与您的示例保持一致,四舍五入将创建14个大小为1的列表,四舍五入将创建7个大小为2的列表。

真正的解决方案只能通过不首先计算批处理大小来完成:

public static <T> List<List<T>> partition(List<T> ascSortedItems, int numLists) {
    if(numLists < 2) {
        if(numLists < 1) throw new IllegalArgumentException();
        return Collections.singletonList(ascSortedItems);
    }
    int listSize = ascSortedItems.size();
    if(listSize <= numLists) {
        return ascSortedItems.stream()
            .map(Collections::singletonList)
            .collect(Collectors.toList());
    }
    return IntStream.range(0, numLists)
        .mapToObj(i -> ascSortedItems.subList(i*listSize/numLists, (i+1)*listSize/numLists))
        .collect(Collectors.toList());
}

第一部分检查numLists参数的有效性,并处理一个列表的特殊情况。

如果源列表小于请求的列表数,则中间部分处理您返回较少列表的要求(否则,结果将始终具有请求的列表数,可能包含空列表)。

最后一部分完成实际工作。 如您所见,初始IntStream.range(0, numLists)始终会产生numLists元素,然后将其映射到子列表,从而将舍入时间推迟到可能的最新点。 对于您的[ 1, 2, 3, 4]和三个请求列表的示例,它将产生

[1]
[2]
[3, 4]

对于14个元素并请求十个列表,它会产生

[1]
[2]
[3, 4]
[5]
[6, 7]
[8]
[9]
[10, 11]
[12]
[13, 14]

不可避免地要有一些大小为1的列表和一些大小为2的列表来满足具有恰好十个列表的请求。 这是第一个基于大小目标的解决方案的根本区别,在该解决方案中,一个列表最多具有与其他列表不同的大小。

n是原始列表中的元素数
z是网格大小
s = z/n (整数除法)给出每个数组应容纳的基本项数
r是整数除法的余数

对第一个zr数组运行循环,以正确的顺序附加每个s项目

运行最后一个r数组的第二个循环,每个数组以正确的顺序附加s + 1项。

假设:以下答案仅适用于有序和顺序流。 该问题要求将排序后的列表(有序的)分成较小的排序后的列表。

如果流是无序的,则必须通过Stream.sorted()之类的方式对流进行排序。


让我们构造一个包含17个元素的列表。

List<Long> sourceList = LongStream.range(0,17).collect(ArrayList::new,ArrayList::add,ArrayList::addAll);

要将17个元素分为10个列表的相等集合,我们将必须制作7个2个元素的列表和3个1个元素的列表。

换句话说,可以使用最少1个元素创建10个相等的列表。 并且可以在前7个列表中添加7个额外的元素。

int minElementinEachList = sourceList.size() /10;      //1
int extraElements = sourceList.size() %10;             //7

直到创建了7( extraElements )个列表,我们才能在列表中添加一个额外的元素。 使用以下代码:

AtomicInteger counter = new AtomicInteger(0);
Map<Number,List<Number>> map = sourceList.stream().collect(
    Collectors.groupingBy(it -> {
    int key = counter.getAndIncrement() / (minElementinEachList + 1);
    if(key >= extraElements && (counter.get() + 1) % (minElementinEachList +1 ) == 0){
        counter.getAndIncrement();
    }
    return key;
}));
System.out.println(map);

输出:

{0=[0, 1], 1=[2, 3], 2=[4, 5], 3=[6, 7], 4=[8, 9], 5=[10, 11], 6=[12, 13], 7=[14], 8=[15], 9=[16]}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM