2

I have an object Foo which contains a list of Bar. The classes are described as follows:

class Foo {
    String name;
    List<Bar> bars = new ArrayList<Bar>();

    Foo(String name){
        this.name = name;
    }
}

class Bar {
    String name;

    Bar(String name){
        this.name = name;
    }
}

Now, i am creating a list of Foo objects, each containing a list of Bar objects as follows:

IntStream
        .range(1, 4)
        .forEach(i -> foos.add(new Foo("Foo" + i)));

foos.forEach(f ->
            IntStream.range(1,4)
                    .forEach(i -> f.bars.add(new Bar("Bar"+i+" -> "+f.name))));

And then using flatMap on a Stream as follows:

foos.stream()
        .flatMap(f -> f.bars.stream())
        .forEach(i -> System.out.println("Bar Name : "+i.name));

How can do all these things in a single execution using Java Stream and lambdas? Is there any other way to do such kind of things with Java 8 style?

Eran
  • 387,369
  • 54
  • 702
  • 768
KayV
  • 12,987
  • 11
  • 98
  • 148

3 Answers3

4

If all you’re going to do, is to print the names of the Bar instances, the entire construction and collection of Foo and Bar instances is obsolete. You can generate and print the names directly:

IntStream.range(1, 4)
         .mapToObj(i -> "Foo" + i)
         .flatMap(name -> IntStream.range(1, 4)
            .mapToObj(i -> "Bar" + i + "->" + name))
         .forEach(System.out::println);
Holger
  • 285,553
  • 42
  • 434
  • 765
1

You can generate the Foo instances in mapToObj instead of forEach :

IntStream.range(1, 4)
         .mapToObj (i -> {Foo foo = new Foo("Foo" + i);
                          foo.bars =
                              IntStream.range(1,4)
                                       .mapToObj(j -> new Bar("Bar"+j+" -> "+foo.name))
                                       .collect(Collectors.toList());
                          return foo;
                    })
         .flatMap (f -> f.bars.stream())
         .forEach(i -> System.out.println("Bar Name : "+i.name));

This, however, won't store the created Foo instances in a List. If you want to store them in a List, either split the operation to 2 Stream pipelines (the first ending in collect(Collectors.toList())) or use peek(f->foos.add(f)) to add the Foo instances into the foos List (which will require that you instantiate the foos List before running the pipeline).

EDIT:

Fixed some typos and tested the code :

Bar Name : Bar1 -> Foo1
Bar Name : Bar2 -> Foo1
Bar Name : Bar3 -> Foo1
Bar Name : Bar1 -> Foo2
Bar Name : Bar2 -> Foo2
Bar Name : Bar3 -> Foo2
Bar Name : Bar1 -> Foo3
Bar Name : Bar2 -> Foo3
Bar Name : Bar3 -> Foo3
Eran
  • 387,369
  • 54
  • 702
  • 768
  • I got the following compile error using your answer: The method mapToObj(IntFunction extends U>) in the type IntStream is not applicable for the arguments (( i) -> {}) – KayV Dec 01 '16 at 09:55
  • 1
    @KaranVerma All is fixed now. – Eran Dec 01 '16 at 10:01
0

I also got the resolution from following pipeline operations using peek and mapToObject...

IntStream.range(1, 4)
            .mapToObj(i -> new Foo("Foo"+i))
            .peek(f -> IntStream.range(1, 4)
                    .mapToObj(i -> new Bar("Bar"+i+"->"+f.name))
                    .forEach(f.bars::add))
            .flatMap(f -> f.bars.stream())
            .forEach(b -> System.out.println("Bar : "+b.name));
KayV
  • 12,987
  • 11
  • 98
  • 148