Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 爪哇8流及;lambdas保持严格的FP_Java_Lambda_Functional Programming_Java 8_Java Stream - Fatal编程技术网

Java 爪哇8流及;lambdas保持严格的FP

Java 爪哇8流及;lambdas保持严格的FP,java,lambda,functional-programming,java-8,java-stream,Java,Lambda,Functional Programming,Java 8,Java Stream,Java 8 lambda在许多情况下非常有用,可以以紧凑的方式以FP方式实现代码。 但是,在某些情况下,我们可能不得不访问/改变外部状态,根据FP实践,这不是一个好的实践。 (因为Java8函数接口有严格的输入和输出签名,所以我们不能传递额外的参数) 例如: class Country{ List<State> states; } class State{ BigInt population; String capit

Java 8 lambda在许多情况下非常有用,可以以紧凑的方式以FP方式实现代码。
但是,在某些情况下,我们可能不得不访问/改变外部状态,根据FP实践,这不是一个好的实践。
(因为Java8函数接口有严格的输入和输出签名,所以我们不能传递额外的参数)

例如:

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

    class Main{
        List<Country> countries;

        //code to fill
    }
国家级{
列出国家名单;
}
阶级国家{
BigInt种群;
字符串资本;
}
班长{
列出国家名单;
//要填写的代码
}
假设用例是获取所有国家的所有首都和所有州的全体人口的列表

正常实施:

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)
    }
}
List capitals=new ArrayList();
BigInt人口=新的BigInt(0);
适用于(国家:国家){
for(州:州){
大写。添加(state.capital);
人口.添加(州.人口)
}
}
如何以更优化的方式在Java8流中实现相同的功能

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

    capitals = statesStream.get().collect(toList());
    population = statesStream.get().reduce((pop1,pop2) -> return pop1+pop2);
streamstatesstream=countries.Stream().flatMap(country->country.getStates());
大写=statesStream.get().collect(toList());
population=statesStream.get().reduce((pop1,pop2)->返回pop1+pop2);

但是上面的实现不是很有效。使用Java 8 Streams操作多个集合的任何其他更好的方法

您只能使用一个流一次,因此您需要创建一个可以减少的聚合:

public class CapitalsAndPopulation {
  private List<String> capitals;
  private BigInt population;

  // constructors and getters omitted for conciseness

  public CapitalsAndPopulation merge(CapitalsAndPopulation other) {
    return new CapitalsAndPopulation(
      Lists.concat(this.capitals, other.capitals),
      this.population + other.population);
  }
}

这看起来很难看的原因是,对于元组或映射之类的结构,您没有很好的语法,因此您需要创建类以使管道看起来很好…

如果您想在一个管道中收集多个结果,您应该创建一个结果容器和一个自定义的
收集器

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;
classmyresult{
私有BigInteger填充=BigInteger.0;
private List capitals=new ArrayList();
公共空间累积(州){
人口=人口.add(state.population);
大写。添加(state.capital);
}
公共MyResult合并(MyResult其他){
人口=人口.add(其他.人口);
大写。addAll(其他。大写);
归还这个;
}
}
MyResult结果=countries.stream()
.flatMap(c->c.getStates().stream())
.collect(Collector.of(MyResult::new,MyResult::acculate,MyResult::merge));
BigInteger总体=result.population;
列表大写字母=result.capitals;
或者流两次,就像你做的那样。

试试这个

class Pair<T, U> {
    T first;
    U second;

    Pair(T first, U second) {
        this.first = first;
        this.second = second;
    }
}

Pair<List<String>, BigInteger> result = countries.stream()
    .flatMap(country -> country.states.stream())
    .collect(() -> new Pair<>(
            new ArrayList<>(),
            BigInteger.ZERO
        ),
        (acc, state) -> {
            acc.first.add(state.capital);
            acc.second = acc.second.add(state.population);
        },
        (a, b) -> {
            a.first.addAll(b.first);
            a.second = a.second.add(b.second);
        });
类对{
T第一;
U秒;
配对(T第一,U第二){
this.first=first;
这个秒=秒;
}
}
配对结果=countries.stream()
.flatMap(country->country.states.stream())
.收集(()->新的一对(
新建ArrayList(),
BigInteger.0
),
(行政协调会,州)->{
根据第一条添加(州首府);
acc.second=acc.second.add(州人口);
},
(a、b)->{
a、 第一,addAll(b.first);
a、 秒=a.second.add(b.second);
});
您可以使用
AbstractMap.Entry
而不是
Pair

Entry result=countries.stream()
.flatMap(country->country.states.stream())
.collect(()->new AbstractMap.SimpleEntry(
新建ArrayList(),
BigInteger.0
),
(行政协调会,州)->{
acc.getKey().add(state.capital);
acc.setValue(acc.getValue().add(state.population));
},
(a、b)->{
a、 getKey().addAll(b.getKey());
a、 setValue(a.getValue().add(b.getValue());
});

构造函数在哪里?为了简洁起见,我省略了它。我对我的答案进行了编辑,使其更为明显。@kewne你说到点子上了,没有简单的方法在flyYou上创建一个单独的结构。你的代码不是“非常有效”,你的代码根本不工作。在
Stream
上没有
get()
方法,streams也不会神奇地为您选择正确的属性。但是在您浪费时间实现这样一个
收集器之前,您应该验证关于效率的最初说法。在列表上迭代并不昂贵,同时做两件完全不同的事情可能会阻碍JIT的优化潜力……我认为在这种情况下,效率意味着不需要两次流式处理。因为这个操作不会花很长时间。是的,但是“流两次”是什么意思?这只意味着在OP声称“效率不高”的源列表上迭代,而无需进一步引用。就开发费用而言,两条流语句比这个收集器简单得多,就执行速度而言,正如前面所说的,没有明确的语句是可能的,但没有理由假设二合一收集器将比流式收集器的性能显著提高两倍。我完全同意。IMO OP的问题是使用Java 8 Streams操作多个集合的任何其他/更好的方法。IMO两次流式处理的意图要模糊得多,特别是当您要执行过滤操作并且必须在两个管道中应用过滤器时。
class Pair<T, U> {
    T first;
    U second;

    Pair(T first, U second) {
        this.first = first;
        this.second = second;
    }
}

Pair<List<String>, BigInteger> result = countries.stream()
    .flatMap(country -> country.states.stream())
    .collect(() -> new Pair<>(
            new ArrayList<>(),
            BigInteger.ZERO
        ),
        (acc, state) -> {
            acc.first.add(state.capital);
            acc.second = acc.second.add(state.population);
        },
        (a, b) -> {
            a.first.addAll(b.first);
            a.second = a.second.add(b.second);
        });
Entry<List<String>, BigInteger> result = countries.stream()
    .flatMap(country -> country.states.stream())
    .collect(() -> new AbstractMap.SimpleEntry<>(
            new ArrayList<>(),
            BigInteger.ZERO
        ),
        (acc, state) -> {
            acc.getKey().add(state.capital);
            acc.setValue(acc.getValue().add(state.population));
        },
        (a, b) -> {
            a.getKey().addAll(b.getKey());
            a.setValue(a.getValue().add(b.getValue()));
        });