どのようにすることができますIチャンク与えられた最大値まで、コレクション内の各アイテムのフィールドのいずれかを合計に基づいて、N個のチャンクに収集、?
例えば、次のように与えられます:
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>>>>
年、月、日(に対応:マップ内のキーはどこFileObject
のmodifiedDate
各ファイルのsizeInBytesの合計がどこにあるか未満)、およびコレクションは、その年、月、日内のすべてのファイルが含まれており、MAX_SIZE_THRESHOLD
。
ループを回避し、ストリーム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
機能(私はしません:-))、私のライブラリを試してみてください:そろばん-Utilの:
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))));