在过滤器函数中使用CompletableFuture

时间:2018-12-07 07:59:44

标签: java lambda java-8 completable-future

我有一个用例,其中我想根据对元素执行的网络调用来过滤列表中的几个元素。为此,我使用了流,过滤器和Completable Future。目标是执行异步执行,以使操作变得高效。伪代码将在下面提到。

public List<Integer> afterFilteringList(List<Integer> initialList){
   List<Integer> afterFilteringList =initialList.stream().filter(element -> {
        boolean valid = true;
        try{
            valid = makeNetworkCallAndCheck().get();
        } catch (Exception e) {

        }
        return valid;
    }).collect(Collectors.toList());

    return afterFilteringList;
}
public CompletableFuture<Boolean> makeNetworkCallAndCheck(Integer value){
   return CompletableFuture.completedFuture(resultOfNetWorkCall(value);
 }

我在这里遇到的问题是,我自己是否以异步方式执行此操作?(因为我在过滤器中使用“ get”功能会阻止执行并使其仅顺序执行)还是存在在Java 8中使用Completable Future和Filters以异步方式完成此操作的更好方法。

3 个答案:

答案 0 :(得分:6)

立即调用get时,确实在破坏异步执行的好处。解决方案是在加入之前先收集所有异步作业。

public List<Integer> afterFilteringList(List<Integer> initialList){
    Map<Integer,CompletableFuture<Boolean>> jobs = initialList.stream()
        .collect(Collectors.toMap(Function.identity(), this::makeNetworkCallAndCheck));
    return initialList.stream()
        .filter(element -> jobs.get(element).join())
        .collect(Collectors.toList());
}
public CompletableFuture<Boolean> makeNetworkCallAndCheck(Integer value){
   return CompletableFuture.supplyAsync(() -> resultOfNetWorkCall(value));
}

当然,方法makeNetworkCallAndCheck也必须启动真正的异步操作。同步调用方法并返回completedFuture是不够的。我在这里提供了一个简单的示例性异步操作,但是对于I / O操作,您可能想提供自己的Executor,以适合您希望允许的同时连接数。

答案 1 :(得分:1)

如果您使用get(),它将不是异步

get()等待该将来完成,然后返回结果。

如果要在异步中处理所有请求。您可以使用CompletetableFuture.allOf()

public List<Integer> filterList(List<Integer> initialList){
    List<Integer> filteredList = Collections.synchronizedList(new ArrayList());
    AtomicInteger atomicInteger = new AtomicInteger(0);
    CompletableFuture[] completableFutures = new CompletableFuture[initialList.size()];
    initialList.forEach(x->{
        completableFutures[atomicInteger.getAndIncrement()] = CompletableFuture
            .runAsync(()->{
                if(makeNetworkCallAndCheck(x)){
                    filteredList.add(x);
                }
        });
    });

    CompletableFuture.allOf(completableFutures).join();
    return filteredList;
}

private Boolean makeNetworkCallAndCheck(Integer value){
    // TODO: write the logic;
    return true;
}

答案 2 :(得分:0)

Collection.parallelStream()是对集合执行异步处理的一种简单方法。您可以按以下方式修改代码:

public List<Integer> afterFilteringList(List<Integer> initialList){
    List<Integer> afterFilteringList =initialList
            .parallelStream()
            .filter(this::makeNetworkCallAndCheck)
            .collect(Collectors.toList());

    return afterFilteringList;
}
public Boolean makeNetworkCallAndCheck(Integer value){
    return resultOfNetWorkCall(value);
}

您可以通过this way自定义自己的执行器。并且根据this确保结果顺序。

我已编写以下代码来验证我的发言。

public class  DemoApplication {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ForkJoinPool forkJoinPool = new ForkJoinPool(50);
        final List<Integer> integers = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            integers.add(i);
        }
        long before = System.currentTimeMillis();
        List<Integer> items = forkJoinPool.submit(() ->
                integers
                        .parallelStream()
                        .filter(it -> {
                            try {
                                Thread.sleep(10000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            return true;
                        })
                        .collect(Collectors.toList()))
                .get();
        long after = System.currentTimeMillis();
        System.out.println(after - before);
    }
}

我创建了自己的ForkJoinPool,尽管每项花费10000毫秒,但我花了10019毫秒来并行完成50个作业。