首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java 8流重构

Java 8流重构
EN

Stack Overflow用户
提问于 2017-09-29 04:29:40
回答 4查看 694关注 0票数 3

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

代码语言:javascript
复制
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;
}

一些类似于Arrays.stream(data).forEach(...)的东西。

提前谢谢。

EN

回答 4

Stack Overflow用户

发布于 2017-09-29 09:31:24

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

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

代码语言:javascript
复制
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());

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

票数 4
EN

Stack Overflow用户

发布于 2017-09-29 05:00:39

值得一提的是,命令式实现可能是表达期望的最有效方式。但是如果你真的想用Java8Stream API实现同样的逻辑,你可以考虑使用.reduce()方法,例如

代码语言:javascript
复制
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;
    }
}

输出

代码语言:javascript
复制
[(5,3), (6,2)]

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

票数 0
EN

Stack Overflow用户

发布于 2017-09-29 12:45:47

您是否正在尝试获取等于指定sum的唯一Pairs

代码语言:javascript
复制
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());

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46477789

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档