Java 8 Java 8流,转换列表<;文件>;映射<;整数,列表<;文件>&燃气轮机;

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

我在传统java循环中有以下代码。希望改用Java8流。 我有一个已排序的文件列表(按文件大小排序)。我将这些文件分组在一起,使所有文件的总大小不超过给定的最大大小,并将它们放在一个带有键1,2,3,。。。等等。这是代码

    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;