Java 爪哇8流及;lambdas保持严格的FP
Java 8 lambda在许多情况下非常有用,可以以紧凑的方式以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
但是,在某些情况下,我们可能不得不访问/改变外部状态,根据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()));
});