Java 如何重新排序CompletableFutures的流?

Java 如何重新排序CompletableFutures的流?,java,java-stream,completable-future,Java,Java Stream,Completable Future,我处理的是一连串可完成的未来。这些需要不同的时间来完成。那些需要更长时间的块流处理,而其他人可能已经完成了(我知道并行流) 因此,我想重新排序流中的项目(例如,使用缓冲区),以向前移动已完成的期货 例如,如果一个getUser调用花费很长时间,则此代码会阻止流处理 public static Boolean isValid(User user) { ... } emails.stream() // not using :: // getUser() returns Completa

我处理的是一连串可完成的未来。这些需要不同的时间来完成。那些需要更长时间的块流处理,而其他人可能已经完成了(我知道并行流)

因此,我想重新排序流中的项目(例如,使用缓冲区),以向前移动已完成的期货

例如,如果一个getUser调用花费很长时间,则此代码会阻止流处理

public static Boolean isValid(User user) { ... }

emails.stream()
   // not using ::
   // getUser() returns CompletableFuture<User>
  .map( e -> getUser(e))
  // this line blocks Stream processing
  .filter( userF -> isValid( userF.get()) )
  .map( f -> f.thenApply(User::getName))
[1] 例如,CompletionService在调用take()时返回已完成的任务,否则返回块。但是CompletionService不接受未来,是否需要执行cs.sumbit(()->f.get())呢

我该怎么做

[编辑]

  • 将示例更改为包含筛选器()
  • 补充评论
  • 添加了CompletionService链接

  • 有更多的背景肯定会有助于调整答案——我感觉问题在其他地方,可以以更简单的方式解决

    但如果你的问题是如何在一开始就以某种方式保持已完成的期货,那么有几种选择:


    使用自定义的
    比较器对
    流进行排序

    .sorted(Comparator.comparing(f -> !f.isDone()))
    
    请记住,
    isDone
    不仅在未来成功完成时返回true


    优先队列中存储期货

    PriorityQueue<CompletableFuture<String>> queue
     = new PriorityQueue<>(Comparator.comparing(f -> !f.isDone()));
    

    有更多的背景肯定会有助于调整答案——我感觉问题在其他地方,可以以更简单的方式解决

    但如果你的问题是如何在一开始就以某种方式保持已完成的期货,那么有几种选择:


    使用自定义的
    比较器对
    流进行排序

    .sorted(Comparator.comparing(f -> !f.isDone()))
    
    请记住,
    isDone
    不仅在未来成功完成时返回true


    优先队列中存储期货

    PriorityQueue<CompletableFuture<String>> queue
     = new PriorityQueue<>(Comparator.comparing(f -> !f.isDone()));
    

    我假设OP中的需求是并发执行
    getUser
    ,并按完成顺序处理结果。以下是执行完成服务的解决方案:

    final CompletionService<User> ecs = new ExecutorCompletionService<>(executor);
    
    emails.stream().map(e -> ecs.submit(() -> getUser(e).get()))
        .collect(Collectors.collectingAndThen(Collectors.toList(), fs -> fs.stream())) // collect the future list for concurrent execution
        .map(f -> {
                try {
                    return ecs.take().get();
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            })
        .filter(u -> isValid(u)).map(User::getName)... //TODO;
    
    final CompletionService ecs=新的ExecutionCompletionService(executor);
    emails.stream().map(e->ecs.submit(()->getUser(e.get()))
    .collect(Collectors.collectingAndThen(Collectors.toList(),fs->fs.stream())//收集未来列表以供并发执行
    .map(f->{
    试一试{
    返回ecs.take().get();
    }捕获(中断异常|执行异常e){
    抛出新的运行时异常(e);
    }
    })
    .filter(u->isValid(u)).map(用户::getName)//待办事项;
    
    或:

    final BlockingQueue=new ArrayBlockingQueue(emails.size());
    final CompletionService ecs=新的ExecutionCompletionService(执行者,队列);
    emails.stream().forEach(e->ecs.submit(()->getUser(e.get());
    IntStream.range(0,emails.size())
    .mapToObj(i->{
    试一试{
    返回queue.poll().get();
    }捕获(中断异常|执行异常e){
    抛出新的运行时异常(e);
    }
    })
    .filter(u->isValid(u)).map(User::getName);
    

    这很简单,但并不简单。

    我假设OP中的需求是并发执行
    getUser
    ,并按完成顺序处理结果。以下是执行完成服务的解决方案:

    final CompletionService<User> ecs = new ExecutorCompletionService<>(executor);
    
    emails.stream().map(e -> ecs.submit(() -> getUser(e).get()))
        .collect(Collectors.collectingAndThen(Collectors.toList(), fs -> fs.stream())) // collect the future list for concurrent execution
        .map(f -> {
                try {
                    return ecs.take().get();
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            })
        .filter(u -> isValid(u)).map(User::getName)... //TODO;
    
    final CompletionService ecs=新的ExecutionCompletionService(executor);
    emails.stream().map(e->ecs.submit(()->getUser(e.get()))
    .collect(Collectors.collectingAndThen(Collectors.toList(),fs->fs.stream())//收集未来列表以供并发执行
    .map(f->{
    试一试{
    返回ecs.take().get();
    }捕获(中断异常|执行异常e){
    抛出新的运行时异常(e);
    }
    })
    .filter(u->isValid(u)).map(用户::getName)//待办事项;
    
    或:

    final BlockingQueue=new ArrayBlockingQueue(emails.size());
    final CompletionService ecs=新的ExecutionCompletionService(执行者,队列);
    emails.stream().forEach(e->ecs.submit(()->getUser(e.get());
    IntStream.range(0,emails.size())
    .mapToObj(i->{
    试一试{
    返回queue.poll().get();
    }捕获(中断异常|执行异常e){
    抛出新的运行时异常(e);
    }
    })
    .filter(u->isValid(u)).map(User::getName);
    

    这很简单,但并不简单。

    为什么不使用
    CompletableFuture::thenapplysync
    ?更改示例以添加筛选器(),使其更清晰。为什么不尝试..stream.parallel()…?为什么不使用
    CompletableFuture::thenapplysync
    ?更改示例以添加筛选器(),使其更清晰。为什么不尝试..stream.parallel()…?使用这样的过滤器,在早期比较时,给定的未来对象F是否可能处于“进行中”状态,在稍后比较时是否可能处于“完成”状态?我想我还记得Java中的默认排序,当这种事情发生时(例如,它在非法状态下失败),比如。但也许我的记忆不准确,这只能发生在设计糟糕的比较算法上……谢谢你的回复。我不确定sorted是否有效,因为调用sorted()时,大多数期货都没有完成,流也没有排序。关于PriorityQueue:那么收集器将添加到PriorityQueue?如何将优先级队列再次转换为流?@StephanSchmidt streams是不可重用的迭代器,因此您始终需要不断创建新实例以获取最新快照-可以从PriorityQueue创建流,但基本相同problem@GPI对这是可能的,但情况总是如此,因为计算需要非零时间,在这段时间内,其他一些计算可能会完成
    final BlockingQueue<Future<User>> queue = new ArrayBlockingQueue<>(emails.size());
    final CompletionService<User> ecs = new ExecutorCompletionService<>(executor, queue);        
    emails.stream().forEach(e -> ecs.submit(() -> getUser(e).get()));
    
    IntStream.range(0, emails.size())
        .mapToObj(i -> {
                try {
                    return queue.poll().get();
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            })
        .filter(u -> isValid(u)).map(User::getName);