扩展现有的流收集器实例

6

我需要一个收集器,它几乎与Collectors.toSet()相同,但具有自定义的完成器。我希望能够像这样做:

myCollector = Collectors.toSet();
myCollector.setFinisher(myCustomFinisher);

完成这个任务似乎不可能。我唯一能想到的替代方案是使用 Collector.of() 重新创建一个类似于 Collectors.toSet() 的方法,但这并不太DRY。
有没有一种方法可以采取现有的Collector并按上面所述进行修改?
编辑
其中一些答案建议像这样做:
  Collector<T, ?, Set<T>> toSet = Collectors.toSet();
  return Collector.of(
     toSet.supplier(),
     toSet.accumulator(),
     toSet.combiner(),
     yourFinisher,
     toSet.characteristics());

然而,我的自定义处理器实际上并没有返回Set;它是使用积累的集合来返回其他内容。事实上,这导致了我陷入了泛型地狱中,我仍在努力理清混乱的代码。

这不是最理想的解决方案,但您可能可以为Collectors创建一个包装类,在其中实现setFinisher - Brandon Laidig
3个回答

12

这正是collectingAndThen(collector,finisher)所做的事情。

自定义的finisher不会替换旧的finisher(如果有),但会与其组合(就像oldFinisher.andThen(newFinisher)一样)。不过,toSet()目前的实现方式并没有finisher(一个identity finisher)。

因此,你所要做的就是 collectingAndThen(toSet(),myCustomFinisher)


4
这是一个基本的toSet实现:

 Collector.of(
            HashSet::new,
            Set::add,
            (left, right) -> {
                left.addAll(right);
                return left;
            });

您所需要做的就是添加UNORDERED特性和完成器。看起来相当简单。或者看一下Holger的答案。


3
按照你问题的字面意思,你不能通过“扩展”返回一个Set的Collector来实现你想要的效果,因为Collector的第三个泛型参数指定了“减少操作的结果类型”,所以一个Collector永远不可能是Collector的子类,除非R实现了Set。
我不知道你想用Set做什么,也不知道你的Set中元素的类型,所以为了简单起见,我们假设你正在流式处理字符串,并且你想通过返回结果为Set的大小来检查流中唯一字符串的数量。
你的Collector的类型必须是Collector,因此你不能绕过这个声明(你想通过Set确定唯一String的数量是不相关的,因此第二个参数可以是?)。
现在,自然地,你想利用Collectors.toSet()。但是怎么做呢?一个Collector有4个函数,一个supplier、一个accumulator、一个combiner和一个finisher。从这4个函数中,似乎你可以使用Collectors.toSet()返回的Collector的前3个函数。现在问题来了:Collectors.toSet()的返回类型实际上是Collector>,其中第二个类型参数?表示累加类型,这是问题所在。换句话说,你无法知道Collector如何在内部累加元素。你只知道finishing function将返回一个Set。然而,这并不一定意味着项目将被累加到Set中。你只知道finishing function将返回一个Set。然而,这并不一定意味着项目将被累加到Set中。你只知道finishing function将返回一个Set。然而,这并不一定意味着项目将被累加到Set中。你只知道finishing function将返回一个Set。然而,这并不一定意味着Collector会将项目累加到Set中。对于你所知道的,Collector可能会在List中累加项目,并且仅在完成阶段从List的内容创建一个Set。另一方面,你自定义的Collector的finishing function计算Set的大小,期望Set作为输入值,但是Collectors.toSet()返回的Collector不能保证它将元素累积到Set中。
很不幸,这意味着如果您想创建一个自定义的Collector将流元素累加到一个Set中,并在完成阶段返回这个Set的大小(或者对这个Set做任何其他你想做的事情),Collectors.toSet()返回的收集器实际上是没有用处的,因为Collector.of(Supplier, BiConsumer, BinaryOperator, Function, Collector.Characteristics)supplieraccumulatorcombiner参数都依赖于积累类型,而Collectors.toSet()并不知道积累类型。
所以,我认为最可行的解决方法是创建一个辅助方法,首先使用Collectors.toSet()将流元素收集到一个Set中,然后手动执行完成转换。通过阅读Holger的答案,似乎已经存在一个执行此操作的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接