如何重用过滤器和应用程序在流上映射? [英] How to reuse application of filter & map on a Stream?

查看:99
本文介绍了如何重用过滤器和应用程序在流上映射?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组从共享类型继承的域对象(即 GroupRecord extends Record RequestRecord extends Record )。子类型具有特定属性(即 GroupRecord :: getCumulativeTime RequestRecord :: getResponseTime )。

I have a set of domain objects that inherit from a shared type (i.e. GroupRecord extends Record, RequestRecord extends Record). The subtypes have specific properties (i.e. GroupRecord::getCumulativeTime, RequestRecord::getResponseTime).

此外,由于解析日志文件,我有一个混合子类型的记录列表。

Further, I have a list of records with mixed subtypes as a result of parsing a logfile.

List<Record> records = parseLog(...);

为了计算日志记录的统计数据,我想仅在子集上应用数学函数与特定子类型匹配的记录,即仅在 GroupRecord s上。因此,我希望有一个特定子类型的过滤流。我知道我可以使用

In order to compute statistics on the log records, I want to apply math functions only on a subset of the records that matches a specific subtype, i.e. only on GroupRecords. Therefore I want to have a filtered stream of specific subtypes. I know that I can apply a filter and map to a subtype using

records.stream()
       .filter(GroupRecord.class::isInstance)
       .map(GroupRecord.class::cast)
       .collect(...

多次在流上应用此过滤器和放置(特别是在执行时)对于不同的计算,它对于相同的子类型多次)不仅繁琐,而且产生大量重复。

Apply this filter&cast on the stream multiple times (especially when doing it for the same subtype multiple times for different computations) is not only cumbersome but produces lots of duplication.

我目前的方法是使用 TypeFilter

class TypeFilter<T>{

    private final Class<T> type;

    public TypeFilter(final Class<T> type) {
        this.type = type;
    }

    public Stream<T> filter(Stream<?> inStream) {
        return inStream.filter(type::isInstance).map(type::cast);
    }
}

要应用于流:

TypeFilter<GroupRecord> groupFilter = new TypeFilter(GroupRecord.class); 

SomeStatsResult stats1 = groupFilter.filter(records.stream())
                                      .collect(...)
SomeStatsResult stats2 = groupFilter.filter(records.stream())
                                      .collect(...)

它有效,但我发现这种方法有点多这么简单的任务。因此我想知道,使用流和函数以简洁和可读的方式使这种行为可重用是否有更好的或最好的方法是什么?

It works, but I find this approach a bit much for such a simple task. Therefore I wonder, is there a better or what is the best way for making this behavior reusable using streams and functions in a concise and readable way?

推荐答案

这取决于你发现什么更简洁和可读。我自己会争辩说你已经实现的方式很好。

It depends on what do you find "more concise and readable". I myself would argue that the way you already implemented is fine as it is.

然而,确实有一种方法可以用稍微缩短的方式来实现使用它的地方,使用 Stream.flatMap

However, there is indeed a way to do this in a way that is slightly shorter from the point of where you use it, by using Stream.flatMap:

static <E, T> Function<E, Stream<T>> onlyTypes(Class<T> cls) {
  return el -> cls.isInstance(el) ? Stream.of((T) el) : Stream.empty();
}

它会做什么,它会将每个原始流元素转换为如果元素具有预期类型,则为一个元素的流,如果不是,则为流。

What it would do is it will convert each original stream element to either a Stream of one element if the element has expected type, or to an empty Stream if it does not.

使用是:

records.stream()
  .flatMap(onlyTypes(GroupRecord.class))
  .forEach(...);

这种方法有明显的权衡:

There are obvious tradeoffs in this approach:


  • 您的管道定义中确实丢失了过滤器字样。这可能比原始版本更令人困惑,因此可能需要比 onlyTypes 更好的名称。

  • Stream 对象相对较重,并且创建这么多对象可能会导致性能下降。但你不应该相信我的话,并在重负荷下描述两种变体。

  • You do lose the "filter" word from your pipeline definition. That may be more confusing that the original, so maybe a better name than onlyTypes is needed.
  • Stream objects are relatively heavyweight, and creating so much of them may result in performance degradation. But you should not trust my word here and profile both variants under heavy load.

编辑

由于该问题要求重新使用过滤器 map 更一般的术语,我觉得这个答案也可以讨论一点抽象。因此,要重复使用过滤器和地图,您需要以下内容:

Since the question asks about reusing filter and map in slightly more general terms, I feel like this answer can also discuss a little more abstraction. So, to reuse filter and map in general terms, you need the following:

static <E, R> Function<E, Stream<R>> filterAndMap(Predicate<? super E> filter, Function<? super E, R> mapper) {
   return e -> filter.test(e) ? Stream.of(mapper.apply(e)) : Stream.empty();
}

原始 onlyTypes 现在实现变为:

static <E, R> Function<E, Stream<R>> onlyTypes(Class<T> cls) {
  return filterAndMap(cls::isInstance, cls::cast);
}

然而,还有一个权衡:现在将产生平面映射器功能在上面的实现中保持捕获两个对象(谓词和映射器)而不是单个对象。它也可能是过度抽象的情况,但这取决于您需要该代码的位置和原因。

But then, there is yet again a tradeoff: resulting flat mapper function will now hold captured two objects (predicate and mapper) instead of single Class object in above implementation. It may also be a case of over-abstracting, but that one depends on where and why you would need that code.

这篇关于如何重用过滤器和应用程序在流上映射?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆