Search code examples
javalambdajava-8java-stream

Java 8 streams, lambdas


I am trying to learn how to utilize Java 8 features(such as lambdas and streams) in my daily programming, since it makes for much cleaner code.

Here's what I am currently working on: I get a string stream from a local file with some data which I turn into objects later. The input file structure looks something like this:

Airport name; Country; Continent; some number;

And my code looks like this:

public class AirportConsumer implements AirportAPI {

List<Airport> airports = new ArrayList<Airport>();

@Override
public Stream<Airport> getAirports() {
    Stream<String> stream = null;
    try {
        stream = Files.lines(Paths.get("resources/planes.txt"));
        stream.forEach(line -> createAirport(line));

    } catch (IOException e) {
        e.printStackTrace();
    }
    return airports.stream();
}

public void createAirport(String line) {
    String airport, country, continent;
    int length;


    airport = line.substring(0, line.indexOf(';')).trim();
    line = line.replace(airport + ";", "");
    country = line.substring(0,line.indexOf(';')).trim();
    line = line.replace(country + ";", "");
    continent = line.substring(0,line.indexOf(';')).trim();
    line = line.replace(continent + ";", "");
    length = Integer.parseInt(line.substring(0,line.indexOf(';')).trim());
    airports.add(new Airport(airport, country, continent, length));
    }
}

And in my main class I iterate over the object stream and print out the results:

public class Main {



public void toString(Airport t){
  System.out.println(t.getName() + " " + t.getContinent());
}

public static void main(String[] args) throws IOException {
    Main m = new Main();
    m.whatever();

}

private void whatever() throws IOException {
    AirportAPI k = new AirportConsumer();
    Stream<Airport> s;
    s = k.getAirports();
    s.forEach(this::toString);

}


}

My question is this: How can I optimize this code, so I don't have to parse the lines from the file separately, but instead create a stream of objects Airport straight from the source file? Or is this the extent in which I can do this?


Solution

  • You need to use map() to transform the data as it comes past.

    Files.lines(Paths.get("resources/planes.txt"))
         .map(line -> createAirport(line));
    

    This will return a Stream<Airport> - if you want to return a List, then you'll need to use the collect method at the end.

    This approach is also stateless, which means you won't need the instance-level airports value.

    You'll need to update your createAirport method to return something:

    public Airport createAirport(String line) {
    
        String airport = line.substring(0, line.indexOf(';')).trim();
        line = line.replace(airport + ";", "");
        String country = line.substring(0,line.indexOf(';')).trim();
        line = line.replace(country + ";", "");
        String continent = line.substring(0,line.indexOf(';')).trim();
        line = line.replace(continent + ";", "");
        int length = Integer.parseInt(line.substring(0,line.indexOf(';')).trim());
        return new Airport(airport, country, continent, length);
    }
    

    If you're looking for a more functional approach to your code, you may want to consider a rewrite of createAirport so it doesn't mutate line. Builders are also nice for this kind of thing.

    public Airport createAirport(final String line) {
        final String[] fields = line.split(";");
        return new Airport(fields[0].trim(), 
                           fields[1].trim(), 
                           fields[2].trim(), 
                           Integer.parseInt(fields[3].trim()));
    }
    

    Throwing it all together, your class now looks like this.

    public class AirportConsumer implements AirportAPI {
    
        @Override
        public Stream<Airport> getAirports() {
            Stream<String> stream = null;
            try {
                stream = Files.lines(Paths.get("resources/planes.txt"))
                                       .map(line -> createAirport(line));
            } catch (IOException e) {
                stream = Stream.empty();
                e.printStackTrace();
            }
            return stream;
        }
    
        private Airport createAirport(final String line) {
            final String[] fields = line.split(";");
            return new Airport(fields[0].trim(), 
                               fields[1].trim(), 
                               fields[2].trim(), 
                               Integer.parseInt(fields[3].trim()));
        }
    }