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

    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;

最佳答案

阅读后,我找到了使用Collectors.groupBy的解决方案。

使用Java8 Lambda表达式的代码

private 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();
    return files.stream().collect(groupingBy((file) -> {
        if (groupSize.addAndGet(file.length()) <= maxSize * MB) {
            return group.get() == 0 ? group.incrementAndGet() : group.get();
        }
        groupSize.set(file.length());
        return group.incrementAndGet();
    }));
}

@Holger提供的代码,然后您可以自由检查组是否等于0
private static final long MB = 1024 * 1024;


private Map<Integer, List<File>> grouping(List<File> files, long maxSize) {
    AtomicInteger group = new AtomicInteger(0);
    //force initializing group starts with 1 even if the first file is empty.
    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();
    }));
}

使用匿名类的代码

受@Holger的启发,所有使用修改外部状态的分组功能的“解决方案”都是滥用API的骇客,因此您可以使用匿名类管理类中的分组逻辑状态。
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();
            }
        };
    }

}

10-04 10:07