【问题标题】:Java 8 streams, lambdasJava 8 流,lambda
【发布时间】:2015-05-27 12:12:51
【问题描述】:

我正在尝试学习如何在我的日常编程中使用 Java 8 功能(例如 lambda 和流),因为它可以使代码更加简洁。

这是我目前正在做的事情: 我从本地文件中获取了一个字符串流,其中包含一些数据,稍后我将其转换为对象。输入文件结构如下所示:

Airport name; Country; Continent; some number;

我的代码如下所示:

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));
    }
}

在我的主类中,我遍历对象流并打印出结果:

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);

}


}

我的问题是:如何优化这段代码,这样我就不必单独解析文件中的行,而是直接从源文件创建一个对象流 Airport?或者这就是我可以做到的程度?

【问题讨论】:

  • 注意:你应该关闭文件:try(Stream&lt;String&gt; lines = Files.lines(...)) { lines.map(xxx).collect(...) };

标签: java lambda java-8 java-stream


【解决方案1】:

您需要使用map() 来转换过去的数据。

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

这将返回一个Stream&lt;Airport&gt; - 如果你想返回一个List,那么你需要在最后使用collect 方法。

这种方法也是无状态的,这意味着您不需要实例级别的 airports 值。

你需要更新你的 createAirport 方法来返回一些东西:

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);
}

如果您正在寻找更实用的代码方法,您可能需要考虑重写createAirport,这样它就不会改变行。建设者也很适合这种事情。

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()));
}

综合起来,你的班级现在看起来像这样。

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()));
    }
}

【讨论】:

  • 假设 createAirport 方法更改为 return 创建的机场,而不是将其附加到该(现已过时的)列表中。
  • 作为补充,我建议阅读java.util.stream 包说明。它不是很长,对理解流真的很有帮助。
  • @tobias_k doh!谢谢 - 已修复。
  • 您的try(…) 语句将在返回之前关闭流,这肯定不是您想要的。 try-with-resources 结构仅对您在语句范围内处理的资源有用,而不是当您想将资源返回给方法的调用者时。顺便说一句,我不明白你为什么将一个类命名为产生 Airportinstances AirportConsumer
  • 但这不是流的工作方式。所有中间操作都是惰性的,并且在接收器(也称为终端操作)需要另一个元素时执行。因此,在终端操作处理最后一个元素之前,最后一行将mapped 到Airport 实例(如果有,因为它可能是短路的)。因此,没有任何地方已经转换了所有行,但保持流打开是有意义的。
【解决方案2】:

Steve 发布的代码看起来很棒。但是还有两个地方可以改进: 1、如何拆分字符串。 2,如果人们忘记或不知道关闭通过调用getAirports()方法创建的流,可能会导致问题。所以最好在原地完成任务(toList() 或其他)。 这是AbacusUtil的代码

try(Reader reader = IOUtil.createBufferedReader(file)) {
    List<Airport> airportList = Stream.of(reader).map(line -> {
        String[] strs = Splitter.with(";").trim(true).splitToArray(line);
        return Airport(strs[0], strs[1], strs[2], Integer.valueOf(strs[3]));
    }).toList();
} catch (IOException e) {
    throw new RuntimeException(e);
}

// 或Try:

List<Airport> airportList = Try.stream(file).call(s -> s.map(line -> {
    String[] strs = Splitter.with(";").trim(true).splitToArray(line);
    return Airport(strs[0], strs[1], strs[2], Integer.valueOf(strs[3]));
}).toList())

披露:我是AbacusUtil的开发者。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 2019-06-04
    • 1970-01-01
    • 2014-05-04
    • 1970-01-01
    • 2015-10-07
    • 1970-01-01
    相关资源
    最近更新 更多