首页 > 解决方案 > 在 Java 流之间共享/重用公共操作序列的最佳方式

问题描述

如果我在两个流中有一个共同的操作序列,如何共享它们以避免重复代码?到目前为止,我发现我可以使用:

Stream<U> commonSequence(Stream<T> upstream) {
    return upstream.op1().op2().op3()
}

然后将其用作

commonSequence(upStream()).downStream()

但是事情并没有真正按照它们发生的顺序来写。我是否忽略了一些可以让我编写如下内容的技巧:

upStream().wrap(commonPart).downStream()

标签: javajava-stream

解决方案


您可以自己制作Collector,但效率可能不高。

下面的收集器有点等价于做filter("foo").limit(10).

class FilterFooAndLimit10Collector<T> implements Collector<T, List<T>, Stream<T>> {
  @Override
  public Supplier<List<T>> supplier() {
    return () -> new ArrayList<>();
  }
  
  @Override
  public BiConsumer<List<T>, T> accumulator() {
    return (list, elem) -> {
      if (!"foo".equals(elem)) list.add(elem);
    };
  }
  
  @Override
  public BinaryOperator<List<T>> combiner() {
    return (list1, list2) -> {
      var res = new ArrayList<T>(list1.size() + list2.size());
      res.addAll(list1);
      res.addAll(list2);
      return res;
    };
  }
  
  @Override
  public Function<List<T>, Stream<T>> finisher() {
    return list -> list.stream().limit(10);
  }
  
  @Override
  public Set<Collector.Characteristics> characteristics() {
    return new HashSet<>();
  }
}

你可以像这样使用它:

Stream
  .of("foo", "bar", "baz", "foo", "quux", "corge", "fred", "waldo", "spam", "ham", "eggs", "foo", "foobar", "xyzzy")
  .collect(new FilterFooAndLimit10Collector<String>())
  .forEach(x -> System.out.println(x));

这将产生输出

bar
baz
quux
corge
fred
waldo
spam
ham
eggs
foobar

当然,您可以修改它以采用构造函数参数以获得更广泛的行为,或者使用其他一些数据结构对其进行优化,但我个人更喜欢commonSequence(stream).


推荐阅读