-1

I have a list of custom object named FileInfo where FileInfo corresponds to

public class FileInfo {
    private String filePath;
    private Long fileSize;
    private String status;
}
List<FileInfo>

I want this list to be broken down into multiple sub lists of FileInfo such that the sum of fileSize of all its element in a sub list is not more that a specific value and the result is a data structure like

List<List<FileInfo>>

I have implemented this solution with regular for each loops but this needs to be done using java streams/Collectors. Any insight would be helpful.

Neha
  • 67
  • 1
  • 7
  • could you post how did you tried to solve the issue with streams/Collectors? Maybe you could also consider using an external library like guava or apache commons? – fascynacja Apr 12 '23 at 06:28
  • I have not done using streams. I was able to implement it with regular for loop – Neha Apr 12 '23 at 06:33
  • 1
    "this needs to be done using java streams" - care to elaborate why? If you've got it running already, what requirement/constraints make you want to use streams? If we understand that we might be better able to help you. In general, I'd say you need a custom collector for this as the standard collectors wouldn't support grouping by changing properties of the groups themselves (i.e. size). – Thomas Apr 12 '23 at 06:46
  • I strongly recommend to read this: https://stackoverflow.com/help/how-to-ask. One of the quotes: "Before posting a question, we strongly recommend that you spend a reasonable amount of time researching the problem and searching for existing questions on this site that may provide an answer. (Stack Overflow has been around for a long time now, so many common questions have already been answered.)" – fascynacja Apr 12 '23 at 07:37

1 Answers1

0

You might need to collect the List<FileInfo> in to map and then convert that into List<List<FileInfo>>. This can be achieved using the Collectors.collectingAndThen, first you can apply Collectors.groupingBy and then customer to convert the map to List<List<FileInfo>>.

In the example below for grouping the file size is added and divided by the FILE_GROUP_LIMIT and quotient is used for gropuing.

Hope that helps.

import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FileInfoProcessor {

  private static Double FILE_GROUP_LIMIT = 200D;

  public static void main(String[] args) throws Exception {
    FileInfoProcessor processor = new FileInfoProcessor();
    List<FileInfo> files = List.of(new FileInfo("A", 100l, "Active"),
        new FileInfo("B", 100l, "Active"),
        new FileInfo("C", 100l, "Active"),
        new FileInfo("D", 100l, "Active"));
    List<List<FileInfo>> finalList = processor.processFiles(files);
    System.out.println(finalList);
  }

  private List<List<FileInfo>> processFiles(List<FileInfo> files) {
    return files
        .stream()
        .collect(
            Collectors.collectingAndThen(
                Collectors.groupingBy(fileInfoFunction(), Collectors.toList()), // This will convert the List<FileInfo> to Map<String, List<FileInfo>>
                map -> map.entrySet().stream().map(entry -> entry.getValue()).collect(Collectors.toList()))); // This will convert Map<String, List<FileInfo>> to List<List<FileInfo>>
  }

  private Function<FileInfo, String> fileInfoFunction() {
    final AtomicLong fileSizeAccumulator = new AtomicLong(0l);
    return (FileInfo file) -> {
      return String.valueOf(Math.ceil((double)fileSizeAccumulator.addAndGet(file.fileSize) / FILE_GROUP_LIMIT)); // This will return the quotient by which the map can be created grouped.
    };
  }

  static class FileInfo {
    private String filePath;
    private Long fileSize;
    private String status;

    public FileInfo(final String filePath, final Long fileSize, final String status) {
      this.filePath = filePath;
      this.fileSize = fileSize;
      this.status = status;
    }

    public String getFilePath() {
      return filePath;
    }

    public void setFilePath(final String filePath) {
      this.filePath = filePath;
    }

    public Long getFileSize() {
      return fileSize;
    }

    public void setFileSize(final Long fileSize) {
      this.fileSize = fileSize;
    }

    public String getStatus() {
      return status;
    }

    public void setStatus(final String status) {
      this.status = status;
    }

    @Override
    public String toString() {
      return "FileInfo{" + "filePath='" + filePath + '\'' + ", fileSize=" + fileSize + ", status='"
          + status + '\'' + '}';
    }
  }

}