Java – how to reuse application filters and maps in streams?

I have a group of domain objects inherited from the shared type (i.e. grouprecord extension record, requestrecord extensions record) Subtypes have specific properties (i.e. grouprecord:: getcumulativetime, requestrecord:: getresponsetime)

In addition, due to parsing log files, I have a list of records with mixed subtypes

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

In order to calculate the statistics of log records, I want to apply mathematical functions only on the subset of records matching the specific subtype (i.e. only on group records) Therefore, I want a filtered stream of a specific subtype I know I can apply filters and map to subtypes

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

Applying this filter to the stream multiple times & cast (especially for different calculations, multiple times for the same subtype) is not only troublesome, but also produces a lot of duplication

My current practice is to use 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);
    }
}

Apply to stream:

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 method a bit like such a simple task So, I wonder if using flows and functions makes this behavior reusable in a concise and understandable way? What's the best way?

Solution

It depends on what you find "more concise and readable" I myself would think that the way you have implemented is good because it is

However, there is a way to do this by using stream Flatmap is a little shorter from the perspective of your use of it:

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

How will it convert each original stream element to a stream of one element, if the element has the expected type, or if not, to an empty stream

Used are:

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

This approach has obvious trade-offs:

>You will lose the word "filter" in the pipe definition This may be more confusing than the original, so perhaps a better name than just type is needed Stream objects are relatively heavyweight, and creating these objects may lead to performance degradation But you should not believe what I said here and analyze the two variants under this heavy load

Edit:

Since the question requires slightly more general reuse of filters and maps, I think this answer can also discuss a little abstraction Therefore, to reuse filters and maps, in general, 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();
}

The original type only implementation is now:

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

However, there is still a tradeoff: the flat mapper function will now capture two objects (predicate and mapper) in the above implementation instead of a single class object This can also be an overly abstract situation, but it depends on where and where you need the code

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>