gpt4 book ai didi

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

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:45:10 26 4
gpt4 key购买 nike

如何根据对集合中每个项目的其中一个字段求​​和,直到给定的最大值,将集合分成 N 个 block ?

例如给出以下内容:

class FileObject { public long sizeInBytes; }
Collection<FileObject> files;
long MAX_SIZE_THRESHOLD = 1024 * 1024 * 100; // 100 MB

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

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

例如

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

我希望最终结果看起来像这样:

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

其中映射中的键是:年月日(对应于FileObjectmodifiedDate),Collection包含该年月日内的所有文件,其中每个文件的 sizeInBytes 小于 MAX_SIZE_THRESHOLD .

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

最佳答案

你可以试试StreamEx.collapse(...)StreamEx .以下是示例代码:

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);

这里是嵌套 groupingBy 的解决方案对于您问题的第 2 部分:

// 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>>>> .

顺便说一句,如果你不想写finisher功能(我没有 :-)),试试我的图书馆:abacus-common :

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))));

关于java - 如何根据项目属性的总和对集合进行分区,直到达到给定的限制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55554029/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com