代码之家  ›  专栏  ›  技术社区  ›  E.S.

用于操作的多个Java消费者

  •  3
  • E.S.  · 技术社区  · 6 年前

    有没有一种方法可以让下面的动作实际上是两个动作合一?

    static int process(String[] cmd, File dir, Consumer<? super String> action) throws IOException {
        ProcessBuilder pb = new ProcessBuilder(cmd);
        pb.directory(dir);
        pb.redirectErrorStream(true);
        Stopwatch sw = Stopwatch.createStarted();
        Process p = pb.start();
        int exit = -1;
        try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
            br.lines().forEach(action);
        } finally {
            log.debug("Ending process {} with exist code {} in time {}", pb.command(),
                    exit = p.destroyForcibly().exitValue(), sw);
        }
        return exit;
    }
    

    所以 Consumer<? super String> action 我通常把它指定为 log::debug 或者自定义函数 AtomicReference::set 但是,如果我想同时成为二者,或者第三者呢?如何让动作执行一些独立的动作?

    我知道我可以创造一个习惯 Consumer 那是我想要的,但我觉得很方便 如果我几乎可以像对待nvarargs等价物一样对待这个动作,但是对于函数/动作来说。

    3 回复  |  直到 6 年前
        1
  •  1
  •   Valentin Ruano    6 年前

    这里有一些选择,其中一些已经被其他人提到了。

    也许最像lambda的方法是使用消费者自己的聚合方法 andThen (定义见 Consumer 本身):

    Stream<X> exes = ....
    exes.forEach(action1
        .andThen(action2)
        .andThen(action3)
        .andThen(...)
        .andThen(actionN));
    

    哪里都有 action? 声明为 Consumer<X> Consumer<? super X> .

    不看文件确认。我猜对于流中的每个元素,动作1到n在同一个线程上一个接一个地执行。

    另一种可能是使用 peek 它基本上将流的元素传递给一个操作,而不使用它们,因为它返回一个包含相同元素的流。

    Stream<X> exes = ...
    exes.peek(action1)
     .peek(action2)
     .peek(action3)
     .peek(...)
     .forEach(actionN);
    

    不看文件,我敢打赌:

    • 你真的需要像 forEach (或) count , empty 等)以执行不同的扫视。
    • 执行顺序中的唯一限制是,只要I<J,对于流中的任何给定元素,Action-I将先于Action-J。除非我们了解流的其他信息(它是并行/串行、排序等),否则无法进行其他假设。

    我想你可能想用 Consumer.andThen 不过,根据你问题的内容 偷看

    也许您希望传递一个修改过的流,该流将“监视”流引用传递到的代码稍后处理的对象。那样的话 偷看 如果不是唯一的选择,那就更好了。

    当然,你也可以两者结合。

        2
  •  2
  •   Louis Wasserman    6 年前

    做一个 Consumer 委托给其他消费者。你只是写

     str -> {
       action1(str);
       action2(str);
       // etc
     }
    

    既然你没有 要使用方法引用,可以始终使用lambdas。写你的 消费者 在这种风格中,也往往比尝试以其他方式组合它们更具可读性,尽管其他方式是可能的。

        3
  •  0
  •   Daniele    6 年前

    通过使用聚合并将消息/请求传递给每个子使用者,您可以在单个使用者中对多个使用者进行分组。(并利用工厂为许多消费者提供服务)。

    class ManyConsumer<T> implements Consumer<T> {
        List<Consumer<T>> list;
        public ManyConsumer(List<Consumer<T>> list)  {
           this.list=list;
        }
        public void accept(T e) {
            for (Consumer<T> c:list) {  
                c.accept(e);
            }
        }
    }