4

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

3
  • 4
    Do you want it to modify the existing Foo objects (that wouldn't be terribly functional...) or create new Foo objects with a sorted list of Bar objects? Commented Oct 5, 2015 at 9:23
  • 3
    You should first think about what you actually want to do. Sort the list contained in Foo.bars in-place or create new instances of Foo. In the former case, just call List.sort on these lists. In the latter case, you have to construct new Foo instances in your map operation. Commented Oct 5, 2015 at 9:24
  • I would like a new list with sorted foos and sorted bars Commented Oct 5, 2015 at 10:00

2 Answers 2

4

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);
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Ok thanks. I had it almost. I just needed to create a new foo in my map operation. Thanks!
You don’t need the curly braces, neither in .peek(f -> { f.bars = expression; }), which can be simply written as .peek(f -> f.bars = expression), nor in .map(f -> { return expression; }), which can be simply written as .map(f -> expression)
3
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());

4 Comments

I will choose this as the best answer since it doesn't need to create a new Foo object and constructor, plus since you are a new user :)
Instead of creating a Bar Stream and a new collection you can simply call Collections.sort.
@zeroflagL: only if you know that the list is mutable. In this case, it’s even easier as you can call sort on the list directly.
@Holger Right. I often forget that Java 8 brought new methods to collections as well. Damned be those default methods ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.