Java 在Filter函数中使用CompletableFuture

Java 在Filter函数中使用CompletableFuture,java,lambda,java-8,completable-future,Java,Lambda,Java 8,Completable Future,我有一个用例,我想根据对元素执行的网络调用过滤掉列表中的几个元素。为了实现这一点,我使用了流、过滤器和Completable Future。目标是执行异步执行,以便操作变得高效。下面提到了这方面的伪代码 public List<Integer> afterFilteringList(List<Integer> initialList){ List<Integer> afterFilteringList =initialList.stream().filt

我有一个用例,我想根据对元素执行的网络调用过滤掉列表中的几个元素。为了实现这一点,我使用了流、过滤器和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和筛选器执行此操作。

如果使用get,它将不会是异步的

get:如有必要,等待此未来完成,然后返回其结果

如果要以异步方式处理所有请求。您可以使用CompletetableFuture.allOf

当您调用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操作,您可能希望提供自己的执行器,根据您希望允许的同时连接的数量进行定制。

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);
}
您可以通过自定义自己的执行器。并且结果的顺序是根据

我已经写了下面的代码来验证我所说的

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,并行完成50个作业需要10019毫秒,尽管每个作业需要10000毫秒。

您忘记将元素传递给makeNetworkCallAndCheck。另外,这个例外情况被认为是“有效的”,这看起来很奇怪。@Holger这个标题与我认为的实际问题不符。我是否以异步方式执行此操作。。。IMHO调用不是异步的,get会被阻塞…想法?@nullpointer好吧,调用get会立即破坏异步执行的好处,毫无疑问,但我不知道,考虑到这段代码,建议什么作为解决方案。例如,输入列表在流操作期间神奇地变成一个列表,最终作为列表返回。我猜,它应该一直是相同的整数对象,但我不想基于假设编写代码…无法保证结果列表与此代码的顺序正确。这里不需要原子整数,因为它不与另一个线程共享。并行流的缺点是对执行器缺乏控制,对于I/O操作,您通常不想要默认池并行性,它是根据CPU内核的数量定制的。@Holger我以前也这么认为,甚至在我的帖子中提到过,直到我看到这篇帖子,这是一个没有官方支持的未记录的副作用。而且它只适用于Fork/Join池,不适用于任意执行器实现。
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);
    }
}