Blog-Archiv

Dienstag, 29. August 2023

Java Functional flatMap Example

Java version 8 (1.8) got a functional extension. That means you can pass around a "function" as method- or even constructor-parameter. The "function" always is a method of an object (or class), and it stays connected to the fields of its object (or class) when passed around.

This is very useful in relation with collections and their predefined functions, but you need to understand the meaning (semantic) of the function to be able to apply it. How would you understand a function named flatMap()? Here is its JavaDoc:

Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is null an empty stream is used, instead.)

An API note tells us a little more:

The flatMap() operation has the effect of applying a one-to-many transformation to the elements of the stream, and then flattening the resulting elements into a new stream.

The function should not be confused with map(), which would not alter the number of elements in the stream, but flatMap() would.

Clarification: both functions do not handle key-value pairs, like java.util.Map does.

Example Code

The flatMap() function is useful wherever a parent-object contains a collection of child-objects. You can turn the stream of parent-objects into a stream of child-objects by flat-mapping. For that, the the flatMap() function requires a parameter that is a function (or lambda) returning the child collection's stream (see line 39).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class FlatMapExample
{
    private static class Room
    {
    }
    
    private static class House
    {
        private final List<Room> rooms = new ArrayList<>();
        
        public List<Room> getRooms() {
            return Collections.unmodifiableList(rooms);
        }
        public void addRoom(Room room) {
            rooms.add(room);
        }
    }
    
    public static void main(String[] args) {
        final House house1 = new House();
        house1.addRoom(new Room());
        
        final House house2 = new House();
        house2.addRoom(new Room());
        house2.addRoom(new Room());
        
        final House house3 = new House();
        house3.addRoom(new Room());
        house3.addRoom(new Room());
        house3.addRoom(new Room());
        
        final List<House> houses = List.of(
                house1, 
                house2, 
                house3);
        
        final long numberOfRooms = houses
                .stream()
                .flatMap(house -> house.getRooms().stream())
                .count();
        
        System.out.println("numberOfRooms = "+numberOfRooms);
    }
}

Line 3 defines a child class definition Room, line 7 defines a parent class definition House. The example uses houses as parent- and rooms as child-objects, a house can contain 0..n rooms.

From line 20 on, some houses are built. The instance house1 contains one room, house2 contains two rooms, house3 contains three rooms (to make it easy:-). All houses are collected into a list of houses on line 32.

Now how can we find out the number of rooms in all houses? The answer is on line 37 and 39. We need to flat-map the house-stream to a room-stream and then count it.

Output of this application is:

numberOfRooms = 6

If you count all rooms between line 21 and 30, you will find out that there are 6, so the result given by the application is correct.

Hope this was helpful!




Keine Kommentare: