Home > Back-end >  Use Java Streams to assemble a list
Use Java Streams to assemble a list

Time:12-21

I have an entity called "File" with the following attributes:

public class File {
    private int id;
    private String proposal;
    private String hash;
    private String path;
    private LocalDateTime createdAt;
    private LocalDateTime finishedAt;
    private int size;
    private boolean processed;
}

I need to organize a queue of which files will be uploaded to a particular service. For each submission, I can send as many files as possible, as long as the sum of their sizes does not exceed 100mb. Furthermore, I must respect the maximum date allowed for each file (which in this case concerns the finishedAt attribute).

I've already managed to sort my list. In other words, I was able to determine which files will be uploaded first:

files.sort(Comparator.comparing(File::getFinishedAt));
files.stream().forEach(file -> System.out.println(file.getId()));

Now I would like to assemble an array with lists of which files will be sorted on each submission. Something like that:

[[1, 4, 7], [8, 2], [6, 3], [5]]

Each "subset" of the above array concerns an upload. And on it are the IDs of my files. So what I would like is for my program to return an array of arrays, where each of the sets contain the largest possible number of files (as long as their sum does not exceed 100mb). As each record has a deadline (finishedAt), they need to be in order as well.

What is the most appropriate way to do this? I'm trying to understand Streams in Java, but the most I could do was sort the list according to date. With the use of Streams I can achieve the result I showed? What is the most appropriate approach to achieve these goals?

Thank you very much!

CodePudding user response:

Stream API has its own limitations and may not be applicable well for stateful operations which occur in the given task: here the sublists should be collected (or appropriate index needs to be incremented) until the size limitation is met, and then the calculation should be computed from the last processed index. That is, there are two state parameters: a total sum and the pair of first/last indexes to define a sublist.

So, good old loop-based solution may be suggested.

Update

Added returning of int[][] array with the ids.

static int[][] groupFiles(List<File> files) {
    files.sort(Comparator.comparing(File::getFinishedAt));
    
    List<int[]> result = new ArrayList<>();
    
    for (int i = 0, n = files.size(); i < n; i  ) {
        int sum = 0;
        int j = i;
        for (; j < n && sum   files.get(j).size <= 100; sum  = files.get(j  ).size);
    
        System.out.println("total size="   sum   "; sublist: ["   i   ", "   j   "]");
        
        List<File> sublist = files.subList(i, j);
        
        sublist.forEach(s -> System.out.println("\t"   s));
        i = --j;
        
        sendFiles(sublist);
        result.add(sublist.stream().mapToInt(File::getId).toArray());
    }
    return result.toArray(new int[0][]);
}

Test:

List<File> files = Arrays.asList(
    new File(1, "f1", LocalDateTime.of(2021, 12, 20, 20, 00), 55),
    new File(2, "f2", LocalDateTime.of(2021, 12, 20, 19, 45), 20),
    new File(3, "f3", LocalDateTime.of(2021, 12, 20, 19, 30), 40),
    new File(4, "f4", LocalDateTime.of(2021, 12, 20, 19, 50), 45),
    new File(5, "f5", LocalDateTime.of(2021, 12, 20, 20, 10), 35)
);
        
System.out.println(Arrays.deepToString(groupFiles(files)));

Output:

total size=60; sublist: [0, 2]
    File: id=3; path=f3; finishedAt=2021-12-20T19:30; size=40
    File: id=2; path=f2; finishedAt=2021-12-20T19:45; size=20
total size=100; sublist: [2, 4]
    File: id=4; path=f4; finishedAt=2021-12-20T19:50; size=45
    File: id=1; path=f1; finishedAt=2021-12-20T20:00; size=55
total size=35; sublist: [4, 5]
    File: id=5; path=f5; finishedAt=2021-12-20T20:10; size=35
[[3, 2], [4, 1], [5]]
  • Related