Java 8 Streams&lambdas维护严格的FP

Sai*_*uri 3 java lambda functional-programming java-8 java-stream

Java 8 lambdas在许多情况下非常有用,可以以紧凑的方式以FP方式实现代码.
但是在某些情况下我们可能不得不访问/改变外部状态,这不是FP实践中的好习惯.
(因为Java 8 Functional接口有严格的输入和输出签名,我们无法传递额外的参数)

例如:

class Country{
        List<State> states;
    }
    class State{
        BigInt population;
        String capital;
    }

    class Main{
        List<Country> countries;

        //code to fill
    }
Run Code Online (Sandbox Code Playgroud)

假设用例是获取所有国家的所有州和所有州的所有州的名单

正常的实施:

List<String> capitals = new ArrayList<>();
BigInt population = new BigInt(0);

for(Country country:countries){
    for(State state:states){
        capitals.add(state.capital);
        population.add(state.population)
    }
}
Run Code Online (Sandbox Code Playgroud)

如何以更优化的方式实现Java 8 Streams?

Stream<State> statesStream = countries.stream().flatMap(country->country.getStates());

    capitals = statesStream.get().collect(toList());
    population = statesStream.get().reduce((pop1,pop2) -> return pop1+pop2);
Run Code Online (Sandbox Code Playgroud)

但是上面的实现效率不高.使用Java 8 Streams操作多个集合的其他更好的方法

Flo*_*own 6

如果要在一个管道中收集多个结果,则应创建结果容器和自定义Collector.

class MyResult {
  private BigInteger population = BigInteger.ZERO;
  private List<String> capitals = new ArrayList<>();

  public void accumulate(State state) {
    population = population.add(state.population);
    capitals.add(state.capital);
  }

  public MyResult merge(MyResult other) {
    population = population.add(other.population);
    capitals.addAll(other.capitals);
    return this;
  }
}
MyResult result = countries.stream()
  .flatMap(c -> c.getStates().stream())
  .collect(Collector.of(MyResult::new, MyResult::accumulate, MyResult::merge));

BigInteger population = result.population;
List<String> capitals = result.capitals;
Run Code Online (Sandbox Code Playgroud)

或者像你一样流两次.