Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Streams - Sort nested lists hierarchically

Given the following example, I would like a stream function that sorts the list and also the nested list

class Foo {
    public int sort;
    public List<Bar> bars;
    public Foo(int sort) {
        this.sort = sort;
    }

}

class Bar {
    public int sort;

    public Bar(int sort) {
        this.sort = sort;
    }
}

@Test
public void testSortering() {
    Foo foo = new Foo(1);
    Foo foo2 = new Foo(2);
    Bar bar = new Bar(1);
    Bar bar2 = new Bar(2);
    foo.bars = Arrays.asList(bar2, bar);
    foo2.bars = Arrays.asList(bar2, bar);

    List<Foo> foos = Arrays.asList(foo2, foo);

    //I would like to iterate foos and return a new foos sorted, and with bars sorted, so that this goes green

    assertEquals(1, foos.get(0).sort);
    assertEquals(1, foos.get(0).bars.get(0).sort);
    assertEquals(2, foos.get(0).bars.get(1).sort);
    assertEquals(2, foos.get(1).sort);
    assertEquals(1, foos.get(1).bars.get(0).sort);
    assertEquals(2, foos.get(1).bars.get(1).sort);

}

I have tried this:

List<List<Bar>> foosSorted = foos.stream()
        .sorted((o1, o2) -> Integer.compare(o1.sort, o2.sort))
        .map(f -> f.bars.stream().sorted((o1, o2) -> Integer.compare(o1.sort, o2.sort)).collect(Collectors.toList()))
        .collect(Collectors.toList());

but this returns Bar, whilst I want a list of Foo

like image 340
Shervin Asgari Avatar asked Sep 10 '25 11:09

Shervin Asgari


2 Answers

The following will sort the foos and the bars for each foo, but since the peek operation is mutating f, this will have unexpected behaviour if parallelism is involved.

List<Foo> foosSorted = foos.stream()
           .sorted(Comparator.comparingInt(o -> o.sort))
           .peek(f -> {
                f.bars = f.bars.stream().sorted(Comparator.comparingInt(o -> o.sort)).collect(Collectors.toList());
            })
            .collect(Collectors.toList());

What I suggest is for you to add a constructor of Foo taking sort and bars and use map instead of peek. This way, we are not mutating any Foo object, so this can be run in parallel without trouble.

List<Foo> foosSorted = foos.stream()
            .sorted(Comparator.comparingInt(o -> o.sort))
            .map(f -> {
                return new Foo(f.sort, f.bars.stream().sorted(Comparator.comparingInt(o -> o.sort)).collect(Collectors.toList()));
            })
            .collect(Collectors.toList());

with:

class Foo {
    public int sort;
    public List<Bar> bars;
    public Foo(int sort) {
        this.sort = sort;
    }
    public Foo(int sort, List<Bar> bars) {
        this.sort = sort;
        this.bars = new ArrayList<>(bars);
    }
}
like image 159
Tunaki Avatar answered Sep 13 '25 01:09

Tunaki


List<Foo> foosSort = foos.stream()
                     .sorted((o1, o2) -> (o1.sort - o2.sort))
                     .map(f -> {
                          List<Bar> bars = f.bars.stream()
                            .sorted((o1, o2) -> (o1.sort- o2.sort))
                            .collect(Collectors.toList());
                          f.bars = bars;
                          return f;
                     })
                     .collect(Collectors.toList());
like image 38
Nagarjuna Avatar answered Sep 13 '25 01:09

Nagarjuna