为了账号安全,请及时绑定邮箱和手机立即绑定

如何根据项目属性的总和对集合进行分区,直至达到给定的限制?

如何根据项目属性的总和对集合进行分区,直至达到给定的限制?

慕丝7291255 2022-09-22 19:23:49

如何根据集合中每个项目的一个字段求和,将集合分块为 N 个块,直至达到给定的最大值?


例如,给定以下内容:


class FileObject { public long sizeInBytes; }

Collection<FileObject> files;

long MAX_SIZE_THRESHOLD = 1024 * 1024 * 100; // 100 MB

我想将项转换为具有最少数量的内部集合的项,并满足谓词,即对于每个集合,每个元素的总和小于 。Collection<Collection<FileObject>>sizeInBytesMAX_SIZE_THRESHOLD


进一步说明,除了上述要求之外,如果扩展为包含时间戳,我还想按年,月和日对结果进行分区。FileObject


例如


class FileObject { public long sizeInBytes; public long modifiedDate; }

我希望最终结果如下所示:


Map<Integer, Map<Integer, Map<Integer, Collection<FileObject>>>>

其中映射中的键是:年、月和日(对应于 的 ),并且集合包含该年、月、日内的所有文件,并且每个文件的大小英寸之和小于 。FileObjectmodifiedDateMAX_SIZE_THRESHOLD


是否可以在避免循环和使用使用 Stream API 或其他可用的函数构造的同时完成这两个操作?两者都可以在一个语句中完成吗?


查看完整描述

1 回答

?
天涯尽头无女友

TA贡献1523条经验 获得超9个赞

您可以在流交易中尝试。下面是示例代码:StreamEx.collapse(...)

final long MAX_SIZE_THRESHOLD = 12; // only for test purpose.


// create the sample file objects with random size for test.

Collection<FileObject> files =

    new Random().longs(0, 1000).limit(50).mapToObj(n -> new FileObject(n % 15, n))

    .collect(Collectors.toList());


// here is the final solution you can try

final MutableLong remaining = MutableLong.of(MAX_SIZE_THRESHOLD);


List<List<FileObject>> result = StreamEx.of(files).collapse((a, b) -> {

  if (b.sizeInBytes <= remaining.value() - a.sizeInBytes) {

    remaining.subtract(a.sizeInBytes);

    return true;

  } else {

    remaining.setValue(MAX_SIZE_THRESHOLD);

    return false;

  }

}, Collectors.toList()).toList();


result.forEach(System.out::println);

以下是您问题的第2部分嵌套的解决方案:groupingBy


// import static java.util.stream.Collectors.*

Map<Integer, Map<Integer, Map<Integer, List<FileObject>>>> result2 = files.stream()

    .filter(f -> f.sizeInBytes < MAX_SIZE_THRESHOLD)

    .collect(groupingBy(f -> f.getYear(), 

                        groupingBy(f -> f.getMonth(), 

                                        groupingBy(f -> f.getDay(), toList()))));


result2.entrySet().forEach(System.out::println);

最后,这是我用来测试的:FileObject


static class FileObject {

  public long sizeInBytes;

  public long modifiedDate;


  public FileObject(long sizeInBytes, long modifiedDate) {

    this.sizeInBytes = sizeInBytes;

    this.modifiedDate = modifiedDate;

  }


  public int getYear() {

    return (int) modifiedDate / 100; // only for test purpose

  }


  public int getMonth() {

    return (int) (modifiedDate % 100) / 10; // only for test purpose

  }


  public int getDay() {

    return (int) modifiedDate % 10; // only for test purpose

  }


  @Override

  public String toString() {

    return sizeInBytes + "-" + modifiedDate;

  }

}

根据评论更新:


您将需要 .Collectors.collectAndThen


Function<List<FileObject>, List<List<FileObject>>> finisher = fileObjs -> {

  MutableLong remaining2 = MutableLong.of(MAX_SIZE_THRESHOLD);

  return StreamEx.of(fileObjs).collapse((a, b) -> {

    if (b.sizeInBytes <= remaining2.value() - a.sizeInBytes) {

      remaining2.subtract(a.sizeInBytes);

      return true;

    } else {

      remaining2.setValue(MAX_SIZE_THRESHOLD);

      return false;

    }

  }, toList()).toList();

};


Map<Integer, Map<Integer, Map<Integer, List<List<FileObject>>>>> result4 = files.stream()

    .collect(groupingBy(f -> f.getYear(),

        groupingBy(f -> f.getMonth(), 

            groupingBy(f -> f.getDay(), collectingAndThen(toList(), finisher)))));

结果类型应为 ,而不是 。Map<Integer, Map<Integer, Map<Integer, List<List<FileObject>>>>>Map<Integer, Map<Integer, Map<Integer, List<FileObject>>>>


顺便说一句,如果你不想写函数(我不:-)),试试我的库:算盘-Util:finisher


Function<List<FileObject>, List<List<FileObject>>> finisher2 = fileObjs -> Seq.of(fileObjs)

    .split(MutableLong.of(0), (f, sizeSum) -> sizeSum.addAndGet(f.sizeInBytes) <= MAX_SIZE_THRESHOLD,

        sizeSum -> sizeSum.setValue(0));


// import static com.landawn.abacus.util.stream.Collectors.MoreCollectors.*;

StreamEx.of(files)

    .toMap(f -> f.getYear(),

        groupingBy(f -> f.getMonth(),

            groupingBy(f -> f.getDay(), collectingAndThen(toList(), finisher2))));




查看完整回答
反对 回复 2022-09-22

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信