Java 8 Java 8流,转换列表<;文件>;映射<;整数,列表<;文件>&燃气轮机;
我在传统java循环中有以下代码。希望改用Java8流。 我有一个已排序的文件列表(按文件大小排序)。我将这些文件分组在一起,使所有文件的总大小不超过给定的最大大小,并将它们放在一个带有键1,2,3,。。。等等。这是代码Java 8 Java 8流,转换列表<;文件>;映射<;整数,列表<;文件>&燃气轮机;,java-8,java-stream,Java 8,Java Stream,我在传统java循环中有以下代码。希望改用Java8流。 我有一个已排序的文件列表(按文件大小排序)。我将这些文件分组在一起,使所有文件的总大小不超过给定的最大大小,并将它们放在一个带有键1,2,3,。。。等等。这是代码 List<File> allFilesSortedBySize = getListOfFiles(); Map<Integer, List<File>> filesGroupedByMaxSizeMap = new HashM
List<File> allFilesSortedBySize = getListOfFiles();
Map<Integer, List<File>> filesGroupedByMaxSizeMap = new HashMap<Integer, List<File>>();
double totalLength = 0L;
int count = 0;
List<File> filesWithSizeTotalMaxSize = Lists.newArrayList();
//group the files to be zipped together as per maximum allowable size in a map
for (File file : allFilesSortedBySize) {
long sizeInBytes = file.length();
double sizeInMb = (double)sizeInBytes / (1024 * 1024);
totalLength = totalLength + sizeInMb;
if(totalLength <= maxSize) {
filesWithSizeTotalMaxSize.add(file);
} else {
count = count + 1;
filesGroupedByMaxSizeMap.put(count, filesWithSizeTotalMaxSize);
filesWithSizeTotalMaxSize = Lists.newArrayList();
filesWithSizeTotalMaxSize.add(file);
totalLength = sizeInMb;
}
}
filesGroupedByMaxSizeMap.put(count+1, filesWithSizeTotalMaxSize);
return filesGroupedByMaxSizeMap;
列出所有文件SortedBySize=getListOfFiles();
Map filesGroupedByMaxSizeMap=new HashMap();
双倍总长度=0L;
整数计数=0;
List filesWithSizeTotalMaxSize=Lists.newArrayList();
//根据映射中允许的最大大小将要压缩的文件分组
for(文件:allFilesSortedBySize){
long sizeInBytes=file.length();
double sizeInMb=(double)sizeInBytes/(1024*1024);
总长度=总长度+sizeInMb;
如果(totalLength)在阅读后,我使用Collectors.groupBy
找到了解决方案
使用java8 lambda表达式的代码
private final long MB=1024*1024;
专用映射分组(列表文件,长maxSize){
AtomicInteger组=新的AtomicInteger(0);
AtomicLong groupSize=新的AtomicLong();
return files.stream().collect(groupingBy((文件)->{
if(groupSize.addAndGet(file.length()){
如果(groupSize.addAndGet(file.length())由于每个元素的处理高度依赖于之前的“处理”,则此任务不适用于流。您仍然可以使用自定义收集器来实现它,但实现将比循环解决方案复杂得多
换句话说,当您将其重写为流操作时,没有任何改进。请继续循环
但是,仍有一些地方可以改进
List<File> allFilesSortedBySize = getListOfFiles();
// get maxSize in bytes ONCE, instead of converting EACH size to MiB
long maxSizeBytes = (long)(maxSize * 1024 * 1024);
// use "diamond operator"
Map<Integer, List<File>> filesGroupedByMaxSizeMap = new HashMap<>();
// start with "create new list" condition to avoid code duplication
long totalLength = maxSizeBytes;
// count is obsolete, the map maintains a size
// the initial "totalLength = maxSizeBytes" forces creating a new list within the loop
List<File> filesWithSizeTotalMaxSize = null;
for(File file: allFilesSortedBySize) {
long length = file.length();
if(maxSizeBytes-totalLength <= length) {
filesWithSizeTotalMaxSize = new ArrayList<>(); // no utility method needed
// store each list immediately, so no action after the loop needed
filesGroupedByMaxSizeMap.put(filesGroupedByMaxSizeMap.size()+1,
filesWithSizeTotalMaxSize);
totalLength = 0;
}
totalLength += length;
filesWithSizeTotalMaxSize.add(file);
}
return filesGroupedByMaxSizeMap;
列出所有文件SortedBySize=getListOfFiles();
//以字节为单位获取maxSize一次,而不是将每个大小转换为MiB
长maxSizeBytes=(长)(maxSize*1024*1024);
//使用“菱形运算符”
Map filesGroupedByMaxSizeMap=new HashMap();
//从“创建新列表”条件开始,以避免代码重复
长总长度=最大尺寸字节;
//计数已过时,贴图将保持一个大小
//初始的“totalLength=maxSizeBytes”强制在循环中创建一个新列表
列出SizeTotalMaxSize=null的文件;
for(文件:allFilesSortedBySize){
long length=file.length();
if(maxSizeBytes totalLength new ArrayList());
但是,对于这是否是一种改进,可能会有不同的看法。我能想到的解决这个问题的最简单方法是使用AtomicLong
包装器表示大小,使用AtomicInteger
包装器表示长度。这些方法对于对它们执行基本算术运算非常有用在这种特殊情况下
List<File> files = getListOfFiles();
AtomicLong length = new AtomicLong();
AtomicInteger index = new AtomicInteger(1);
long maxLength = SOME_ARBITRARY_NUMBER;
Map<Integer, List<File>> collect = files.stream().collect(Collectors.groupingBy(
file -> {
if (length.addAndGet(file.length()) <= maxLength) {
return index.get();
}
length.set(file.length());
return index.incrementAndGet();
}
));
return collect;
List files=getListOfFiles();
AtomicLong长度=新的AtomicLong();
AtomicInteger索引=新的AtomicInteger(1);
long maxLength=一些任意数;
Map collect=files.stream().collect(Collectors.groupingBy(
文件->{
if(length.addAndGet(file.length())在else
中创建新的ArrayList是一个bug还是一个额外的功能?一旦在映射中添加了带有SizeTotalMaxSize的文件,我想为下一次迭代创建一个新的ArrayList。@NewQueries我已经修复了我的答案,你可以看到我是对的吗?我的英语不好,所以如果我错了,请告诉我。@NewQueries嗨,几分钟后我发现t分组方法有一个错误,我已经修复了它,并给你所有的测试,我希望它能帮助你。谢谢你的回答。我同意你。但我想知道分组是否可以使用。不是真的。所有的“解决方案”使用修改外部状态的分组函数是滥用API的黑客,这取决于流在顺序上下文中处理收集器的方式(不用说,它们在并行上下文中会严重中断)。同样,当您尝试将此收集器与其他收集器组合时,它们也会中断。与直接循环相比,它们的效率非常低。@Holger Hi,“所有使用分组函数修改外部状态的“解决方案”都是滥用API的黑客”但您可以使用匿名类来管理自己的状态。@Holger和他的问题不能在并行上下文中使用,这将导致不同的组。即使使用循环将文件按maxSize分组,因为文件必须按顺序分组。@holi java:由于循环从不并行运行,所以您不会尝试将它们切换为并行。这是正确的与流不同,流可以通过单个方法调用转换为并行。如上所述,当与其他收集器组合时,这些groupingBy
也将在顺序上下文中中断。例如,groupingBy(someFunc,groupingBy(anotherFunc))
。如果回答的函数是anotherFunc
,则解决方案将中断(只要外部操作找到多个组)。我必须使用groupSize.set(file.length())而不是set(0)和新的AtomicInteger(1)谢谢我的使用。谢谢你的回答。@newquerys对不起,我有点累了,因为我凌晨3点在哪里。所以仍然有一个bug。我修复了它,但我可以告诉你,你不能用AtomicInteger(1)初始化因为如果第一个文件大小大于maxSize,则第一个组从2
开始,而不是1
。您可以像在我的循环变量中一样,使用maxSize
初始化groupSize
(以字节为单位),这样第一个文件将始终创建一个新组。这样,您可以保持组的初始值为零,但不需要在当前(文件)
中使用条件(同样,lambda变量中不需要进行零测试).@Holger非常感谢,很好的解决方案。我会用你的循环变量更新我的答案。@Holger如果第一个文件是空文件,仍然有一个小问题。我已经解决了。
private static final long MB = 1024 * 1024;
private Map<Integer, List<File>> grouping(List<File> files, long maxSize) {
return files.stream().collect(groupingBy(groupSize(maxSize)));
}
private Function<File, Integer> groupSize(final long maxSize) {
long maxBytesSize = maxSize * MB;
return new Function<File, Integer>() {
private int group;
private long groupSize = maxBytesSize + 1;
@Override
public Integer apply(File file) {
return hasRemainingFor(file) ? current(file) : next(file);
}
private boolean hasRemainingFor(File file) {
return (groupSize += file.length()) <= maxBytesSize;
}
private int next(File file) {
groupSize = file.length();
return ++group;
}
private int current(File file) {
return group;
}
};
}
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.groupingBy;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
/**
* Created by holi on 3/24/17.
*/
public class StreamGroupingTest {
private final File FILE_1MB = file(1);
private final File FILE_2MB = file(2);
private final File FILE_3MB = file(3);
@Test
void eachFileInIndividualGroupIfEachFileSizeGreaterThanMaxSize() {
Map<Integer, List<File>> groups = grouping(asList(FILE_2MB, FILE_3MB), 1);
assertThat(groups.size(), equalTo(2));
assertThat(groups.get(1), equalTo(singletonList(FILE_2MB)));
assertThat(groups.get(2), equalTo(singletonList(FILE_3MB)));
}
@Test
void allFilesInAGroupIfTotalSizeOfFilesLessThanOrEqualMaxSize() {
Map<Integer, List<File>> groups = grouping(asList(FILE_2MB, FILE_3MB), 5);
assertThat(groups.size(), equalTo(1));
assertThat(groups.get(1), equalTo(asList(FILE_2MB, FILE_3MB)));
}
@Test
void allNeighboringFilesInAGroupThatTotalOfTheirSizeLessThanOrEqualMaxSize() {
Map<Integer, List<File>> groups = grouping(asList(FILE_1MB, FILE_2MB, FILE_3MB), 3);
assertThat(groups.size(), equalTo(2));
assertThat(groups.get(1), equalTo(asList(FILE_1MB, FILE_2MB)));
assertThat(groups.get(2), equalTo(singletonList(FILE_3MB)));
}
@Test
void eachFileInIndividualGroupIfTheFirstFileAndTotalOfEachNeighboringFilesSizeGreaterThanMaxSize() {
Map<Integer, List<File>> groups = grouping(asList(FILE_2MB, FILE_1MB, FILE_3MB), 2);
assertThat(groups.size(), equalTo(3));
assertThat(groups.get(1), equalTo(singletonList(FILE_2MB)));
assertThat(groups.get(2), equalTo(singletonList(FILE_1MB)));
assertThat(groups.get(3), equalTo(singletonList(FILE_3MB)));
}
@Test
void theFirstEmptyFileInGroup1() throws Throwable {
File emptyFile = file(0);
Map<Integer, List<File>> groups = grouping(singletonList(emptyFile), 2);
assertThat(groups.get(1), equalTo(singletonList(emptyFile)));
}
private static final long MB = 1024 * 1024;
private Map<Integer, List<File>> grouping(List<File> files, long maxSize) {
AtomicInteger group = new AtomicInteger(0);
AtomicLong groupSize = new AtomicLong(maxSize * MB + 1);
return files.stream().collect(groupingBy((file) -> {
if (groupSize.addAndGet(file.length()) <= maxSize * MB) {
return group.get();
}
groupSize.set(file.length());
return group.incrementAndGet();
}));
}
private Function<File, Integer> groupSize(final long maxSize) {
long maxBytesSize = maxSize * MB;
return new Function<File, Integer>() {
private int group;
private long groupSize = maxBytesSize + 1;
@Override
public Integer apply(File file) {
return hasRemainingFor(file) ? current(file) : next(file);
}
private boolean hasRemainingFor(File file) {
return (groupSize += file.length()) <= maxBytesSize;
}
private int next(File file) {
groupSize = file.length();
return ++group;
}
private int current(File file) {
return group;
}
};
}
private File file(int sizeOfMB) {
return new File(String.format("%dMB file", sizeOfMB)) {
@Override
public long length() {
return sizeOfMB * MB;
}
@Override
public boolean equals(Object obj) {
File that = (File) obj;
return length() == that.length();
}
};
}
}
List<File> allFilesSortedBySize = getListOfFiles();
// get maxSize in bytes ONCE, instead of converting EACH size to MiB
long maxSizeBytes = (long)(maxSize * 1024 * 1024);
// use "diamond operator"
Map<Integer, List<File>> filesGroupedByMaxSizeMap = new HashMap<>();
// start with "create new list" condition to avoid code duplication
long totalLength = maxSizeBytes;
// count is obsolete, the map maintains a size
// the initial "totalLength = maxSizeBytes" forces creating a new list within the loop
List<File> filesWithSizeTotalMaxSize = null;
for(File file: allFilesSortedBySize) {
long length = file.length();
if(maxSizeBytes-totalLength <= length) {
filesWithSizeTotalMaxSize = new ArrayList<>(); // no utility method needed
// store each list immediately, so no action after the loop needed
filesGroupedByMaxSizeMap.put(filesGroupedByMaxSizeMap.size()+1,
filesWithSizeTotalMaxSize);
totalLength = 0;
}
totalLength += length;
filesWithSizeTotalMaxSize.add(file);
}
return filesGroupedByMaxSizeMap;
filesWithSizeTotalMaxSize = new ArrayList<>();
filesGroupedByMaxSizeMap.put(filesGroupedByMaxSizeMap.size()+1,
filesWithSizeTotalMaxSize);
filesWithSizeTotalMaxSize = filesGroupedByMaxSizeMap.computeIfAbsent(
filesGroupedByMaxSizeMap.size()+1, x -> new ArrayList<>());
List<File> files = getListOfFiles();
AtomicLong length = new AtomicLong();
AtomicInteger index = new AtomicInteger(1);
long maxLength = SOME_ARBITRARY_NUMBER;
Map<Integer, List<File>> collect = files.stream().collect(Collectors.groupingBy(
file -> {
if (length.addAndGet(file.length()) <= maxLength) {
return index.get();
}
length.set(file.length());
return index.incrementAndGet();
}
));
return collect;