Java 8 Java8流重构

Java 8 Java8流重构,java-8,refactoring,java-stream,Java 8,Refactoring,Java Stream,是否可以使用Java 8流结构更简洁地表达以下逻辑: public static Set<Pair> findSummingPairsLookAhead(int[] data, int sum){ Set<Pair> collected = new HashSet<>(); Set<Integer> lookaheads = new HashSet<>(); for(int i = 0; i < data

是否可以使用Java 8流结构更简洁地表达以下逻辑:

public static Set<Pair> findSummingPairsLookAhead(int[] data, int sum){
    Set<Pair> collected = new HashSet<>();
    Set<Integer> lookaheads = new HashSet<>();

    for(int i = 0; i < data.length; i++) {
        int elem = data[i];
        if(lookaheads.contains(elem)) {
            collected.add(new Pair(elem, sum - elem));
        }
        lookaheads.add(sum - elem);
    }

    return collected;
}
公共静态集合findSummingPairsLookAhead(int[]数据,int总和){
Set collected=新的HashSet();
Set lookaheads=new HashSet();
对于(int i=0;i
对数组.stream(data).forEach(…)有影响的东西


提前感谢。

您所拥有的一切都很好(java-8神也很高兴)。主要的问题是,您依赖于副作用,streams对此并不满意——他们甚至在文档中明确提到了这一点

我可以想到这一点(我用
SimpleEntry
替换了
Pair
,这样我就可以编译了)

公共静态集合findSummingPairsLookAhead2(int[]数据,int总和){
Set lookaheads=Collections.synchronizedSet(newhashset());
返回数组.stream(数据)
.boxed()
.map(x->{
lookaheads.add(总和-x);
返回x;
})
.filter(lookaheads::contains)
.collect(收集器.mapping)(
x->newAbstractMap.SimpleEntry(x,sum-x),
Collectors.toSet());
}
但我们仍在以安全的方式打破
map
的副作用特性,但仍然很糟糕。想想那些会来找你的人,看看这些代码;他们可能会觉得这至少很奇怪


如果从未计划并行运行此操作,则可以删除
集合.synchronizedSet
——但风险自负

值得一提的是,命令式实施可能是表达期望的最有效方式。但是如果你真的想用java 8流API实现相同的逻辑,你可以考虑使用<代码>还原()/<代码>方法,例如

import org.apache.commons.lang3.tuple.Pair;

import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;  

final class SummingPairsLookAheadExample {

    public static void main(String[] args) {
        final int[] data = new int[]{1,2,3,4,5,6};
        final int sum = 8;

        final Set<Pair> pairs = Arrays.stream(data)
                .boxed()
                .parallel()
                .reduce(
                        Pair.of(Collections.synchronizedSet(new HashSet<Pair>()), Collections.synchronizedSet(new HashSet<Integer>())),
                        (pair,el) -> doSumming(pair, el, sum),
                        (a,b) -> a
                ).getLeft();

        System.out.println(pairs);
    }

    synchronized private static Pair<Set<Pair>, Set<Integer>> doSumming(Pair<Set<Pair>, Set<Integer>> pair, int el, int sum) {
        if (pair.getRight().contains(el)) {
            pair.getLeft().add(Pair.of(el, sum - el));
        }
        pair.getRight().add(sum - el);
        return pair;
    }
}

.reduce()
方法中的第一个参数是累加器的初始值。此对象将传递给每个迭代步骤。在本例中,我们使用了一对
Set
(预期结果)和
Set
(与示例中的变量
lookaheads
相同)。第二个参数是执行逻辑的lambda(
BiFunction
)(提取到单独的私有方法以使代码更紧凑)。最后一个是二进制运算符。这是相当冗长的,但它不依赖于任何副作用。指出我之前的示例在并行执行方面存在问题,所以我已经更新了这个示例,使其在并行执行中也安全。如果您不并行运行它,您可以简单地从helper方法中删除
synchronized
关键字,并使用正则集而不是synchronized集作为累加器的初始值。

在迭代过程中涉及改变状态的算法不太适合流。然而,通常可以根据不显式改变任何中间状态的批量操作来重新考虑算法

在您的情况下,任务是收集一组
对(x,sum-x)
,其中
sum-x
出现在列表中
x
之前。因此,我们可以首先构建一个数字映射到它们在列表中第一次出现的索引,然后使用该映射过滤列表并构建一组对:

Map<Integer, Integer> firstIdx = IntStream.range(0, data.length)
                         .boxed()
                         .collect(toMap(i -> data[i], i -> i, (a, b) -> a));

Set<Pair> result = IntStream.range(0, data.length)
                         .filter(i -> firstIdx.contains(sum - data[i]))
                         .filter(i -> firstIdx.get(sum - data[i]) < i)
                         .mapToObj(i -> new Pair(data[i], sum - data[i]))
                         .collect(toSet());
Map firstIdx=IntStream.range(0,data.length)
.boxed()
.collect(toMap(i->data[i],i->i,(a,b)->a));
设置结果=IntStream.range(0,data.length)
.filter(i->firstIdx.contains(sum-data[i]))
.filter(i->firstIdx.get(sum-data[i])新对(数据[i],总和-data[i]))
.收集(toSet());

您可以使用
&&
getOrDefault
来缩短这两个过滤器,如果您觉得更清晰的话。

您是否试图获得唯一的
,其和等于指定的

Arrays.stream(data).boxed()
        .collect(Collectors.groupingBy(i -> i <= sum / 2 ? i : sum - i, toList())).values().stream()
        .filter(e -> e.size() > 1 && (e.get(0) * 2 == sum || e.stream().anyMatch(i -> i == sum - e.get(0))))
        .map(e -> Pair.of(sum - e.get(0), e.get(0)))
        .collect(toList());
Arrays.stream(data.boxed())
.collect(Collectors.groupingBy(i->i e.size()>1&&(e.get(0)*2==sum|e.stream().anyMatch(i->i==sum-e.get(0)))
.map(e->Pair.of(和-e.get(0),e.get(0)))
.collect(toList());

返回一个具有唯一对的列表。如果需要,您可以将其更改为set by
toSet()

很好的尝试,但这里有竞争条件。尝试添加
parallel
,看看它是如何实现的breaks@Eugene很好的捕获,谢谢Eugene!我已经修复了代码,可以在并行执行中正确运行。
Map<Integer, Integer> firstIdx = IntStream.range(0, data.length)
                         .boxed()
                         .collect(toMap(i -> data[i], i -> i, (a, b) -> a));

Set<Pair> result = IntStream.range(0, data.length)
                         .filter(i -> firstIdx.contains(sum - data[i]))
                         .filter(i -> firstIdx.get(sum - data[i]) < i)
                         .mapToObj(i -> new Pair(data[i], sum - data[i]))
                         .collect(toSet());
Arrays.stream(data).boxed()
        .collect(Collectors.groupingBy(i -> i <= sum / 2 ? i : sum - i, toList())).values().stream()
        .filter(e -> e.size() > 1 && (e.get(0) * 2 == sum || e.stream().anyMatch(i -> i == sum - e.get(0))))
        .map(e -> Pair.of(sum - e.get(0), e.get(0)))
        .collect(toList());