Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.programmer > #33854 > unrolled thread

stream filter twice

Started byPhilipp Kraus <philipp.kraus.flashpixx@gmail.com>
First post2016-02-20 04:28 -0800
Last post2016-02-22 00:13 +0100
Articles 6 — 5 participants

Back to article view | Back to comp.lang.java.programmer


Contents

  stream filter twice Philipp Kraus <philipp.kraus.flashpixx@gmail.com> - 2016-02-20 04:28 -0800
    Re: stream filter twice Eric Sosman <esosman@comcast-dot-net.invalid> - 2016-02-20 08:16 -0500
    Re: stream filter twice Arne Vajhøj <arne@vajhoej.dk> - 2016-02-20 22:20 -0500
      Re: stream filter twice Robert Klemme <shortcutter@googlemail.com> - 2016-02-21 12:58 +0100
        Re: stream filter twice Arne Vajhøj <arne@vajhoej.dk> - 2016-02-21 09:52 -0500
        Re: stream filter twice Sebastian <news@seyweiler.dyndns.org> - 2016-02-22 00:13 +0100

#33854 — stream filter twice

FromPhilipp Kraus <philipp.kraus.flashpixx@gmail.com>
Date2016-02-20 04:28 -0800
Subjectstream filter twice
Message-ID<04334e10-3bad-49dc-87d8-dc2f00f90807@googlegroups.com>
Hello,

I'm using streams to filter some data. I just do this with


List<myClass> x = myclasscollection.stream().filter( i -> i.filter() ).collect( Collectors.toList() );
if (!x.isempty())
   return x;


List<myClass> y = myclasscollection.stream().filter( i -> i.anotherfilter() ).collect( Collectors.toList() );
if (!y.isEmpty())
   return y;

return  myclasscollection.stream().collect( Collectors.toList() );

So my question is, can optimize this with a stream operation. I need to check each object of the collection, but only if no object exist, I need to run the check with another filter.
A change to filter( i -> i.filter() || i.anotherfilter() ) or filter( i-> i.filter() ^ i.anotherfilter() ) is not possible, because I need the empty check of all results.

In a best-case idea I need only one run with the stream and can build the result list in one shoot. Did you have got any idea to optimize this code?

Thanks a lot

Phil

[toc] | [next] | [standalone]


#33855

FromEric Sosman <esosman@comcast-dot-net.invalid>
Date2016-02-20 08:16 -0500
Message-ID<na9oq9$abg$1@dont-email.me>
In reply to#33854
On 2/20/2016 7:28 AM, Philipp Kraus wrote:
> Hello,
>
> I'm using streams to filter some data. I just do this with
>
>
> List<myClass> x = myclasscollection.stream().filter( i -> i.filter() ).collect( Collectors.toList() );
> if (!x.isempty())
>     return x;
>
>
> List<myClass> y = myclasscollection.stream().filter( i -> i.anotherfilter() ).collect( Collectors.toList() );
> if (!y.isEmpty())
>     return y;
>
> return  myclasscollection.stream().collect( Collectors.toList() );
>
> So my question is, can optimize this with a stream operation. I need to check each object of the collection, but only if no object exist, I need to run the check with another filter.
> A change to filter( i -> i.filter() || i.anotherfilter() ) or filter( i-> i.filter() ^ i.anotherfilter() ) is not possible, because I need the empty check of all results.
>
> In a best-case idea I need only one run with the stream and can build the result list in one shoot. Did you have got any idea to optimize this code?

     I'm not sure it would count as an "optimization," but you could
use a grouping collector, something like

	Map<Integer,List<myClass>> groups = myclasscollection.stream()
	    .collect(Collectors.groupingBy(
	        (i) -> i.filter() ? 1 : i.anotherfilter() ? 2 : 0);
	if (!groups.get(1).isEmpty()) return groups.get(1);
	if (!groups.get(2).isEmpty()) return groups.get(2);

However, this doesn't strike me as a good idea.  The original looks
better, unless there are issues not known to me.

-- 
esosman@comcast-dot-net.invalid
"Don't be afraid of work. Make work afraid of you." -- TLM

[toc] | [prev] | [next] | [standalone]


#33857

FromArne Vajhøj <arne@vajhoej.dk>
Date2016-02-20 22:20 -0500
Message-ID<nabaej$1rfq$1@gioia.aioe.org>
In reply to#33854
On 2/20/2016 7:28 AM, Philipp Kraus wrote:
> I'm using streams to filter some data. I just do this with
>
>
> List<myClass> x = myclasscollection.stream().filter( i -> i.filter() ).collect( Collectors.toList() );
> if (!x.isempty())
>     return x;
>
>
> List<myClass> y = myclasscollection.stream().filter( i -> i.anotherfilter() ).collect( Collectors.toList() );
> if (!y.isEmpty())
>     return y;
>
> return  myclasscollection.stream().collect( Collectors.toList() );
>
> So my question is, can optimize this with a stream operation. I need
> to check each object of the collection, but only if no object exist,
> I need to run the check with another filter. A change to filter( i ->
> i.filter() || i.anotherfilter() ) or filter( i-> i.filter() ^
> i.anotherfilter() ) is not possible, because I need the empty check
> of all results.
>
> In a best-case idea I need only one run with the stream and can build
> the result list in one shoot. Did you have got any idea to optimize
> this code?

What is your goal? To produce easily understandable code? To save
a few micro seconds?

If the first then I think you current code is fine.

If the second then you will need to be creative.

Here is my attempt to be creative:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class MultiProcessing {
	public static <T> List<T> original(List<T> lst, Predicate<? super T> 
p1, Predicate<? super T> p2) {
		List<T> x = lst.stream().filter(p1).collect(Collectors.toList());
		if(!x.isEmpty()) return x;
		List<T> y = lst.stream().filter(p2).collect(Collectors.toList());
		if(!y.isEmpty()) return y;
		return lst;
	}
	public static <T> List<T> alternative(List<T> lst, Predicate<? super T> 
p1, Predicate<? super T> p2) {
		List<T> x = new ArrayList<>();
		List<T> y = new ArrayList<>();
		lst.stream().forEach(v -> { if(p1.test(v)) x.add(v); if(x.isEmpty() && 
p2.test(v)) y.add(v); });
		if(!x.isEmpty()) return x;
		if(!y.isEmpty()) return y;
		return lst;
	}
	private static <T> void test(List<T> lst, Predicate<? super T> p1, 
Predicate<? super T> p2) {
		System.out.println(original(lst, p1, p2));
		System.out.println(alternative(lst, p1, p2));
	}
	public static void main(String[] args) {
		List<Integer> lst = Arrays.asList(1, 4, 9, 16);
		test(lst, v -> v > 2, v -> v < 2);
		test(lst, v -> v > 20, v -> v < 2);
		test(lst, v -> v > 20, v -> v < -2);
	}
}

Arne

[toc] | [prev] | [next] | [standalone]


#33858

FromRobert Klemme <shortcutter@googlemail.com>
Date2016-02-21 12:58 +0100
Message-ID<ditn2rF45nnU1@mid.individual.net>
In reply to#33857
On 21.02.2016 04:20, Arne Vajhøj wrote:

> What is your goal? To produce easily understandable code? To save
> a few micro seconds?

That is really the first question to answer.

> If the first then I think you current code is fine.
>
> If the second then you will need to be creative.
>
> Here is my attempt to be creative:

I'm a bit more old fashioned, maybe that's why I do not find the 
advantage of streams so big here.  I was also considering, we might not 
know how large the initial collection is.  So the cost of iterating 
twice could be significant.  So:

     public static List<myClass> test_2(final Collection<myClass> 
myclasscollection) {
         final List<myClass> x = new ArrayList<>();
         final List<myClass> y = new ArrayList<>();

         for (final myClass i : myclasscollection) {
             if (i.filter())
                 x.add(i);
             else if (i.anotherfilter()) // "else" is optimization!
                 y.add(i);
         }

         if (!x.isEmpty()) {
             return x;
         } else if (!y.isEmpty()) {
             return y;
         } else
             return new ArrayList<>(myclasscollection);
     }

     public static List<myClass> test_3(final Collection<myClass> 
myclasscollection) {
         final List<myClass> x = new ArrayList<>();
         final List<myClass> y = new ArrayList<>();

         for (final myClass i : myclasscollection) {
             if (i.filter())
                 x.add(i);
             else if (i.anotherfilter()) // "else" is optimization!
                 y.add(i);
         }

         return x.isEmpty() ?
           (y.isEmpty() ? new ArrayList<>(myclasscollection) : y) :
           x;
     }

Note: formatting suffered a bit.

Kind regards

	robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#33859

FromArne Vajhøj <arne@vajhoej.dk>
Date2016-02-21 09:52 -0500
Message-ID<nacium$1fas$1@gioia.aioe.org>
In reply to#33858
On 2/21/2016 6:58 AM, Robert Klemme wrote:
> On 21.02.2016 04:20, Arne Vajhøj wrote:
>> If the first then I think you current code is fine.
>>
>> If the second then you will need to be creative.
>>
>> Here is my attempt to be creative:
>
> I'm a bit more old fashioned, maybe that's why I do not find the
> advantage of streams so big here.

It saves a bit of typing.

>                              I was also considering, we might not
> know how large the initial collection is.  So the cost of iterating
> twice could be significant.

Yes. That is what my alternative tried to address.

>                                                                So:
>

>          for (final myClass i : myclasscollection) {

Stream forEach really does the same as this.

>              if (i.filter())
>                  x.add(i);
>              else if (i.anotherfilter()) // "else" is optimization!
>                  y.add(i);

But if we are in micro-optimization mode, then why add to y when we
know that x is non empty?

Arne

[toc] | [prev] | [next] | [standalone]


#33860

FromSebastian <news@seyweiler.dyndns.org>
Date2016-02-22 00:13 +0100
Message-ID<nadgbe$mj$1@news.albasani.net>
In reply to#33858
Am 21.02.2016 12:58, schrieb Robert Klemme:
> On 21.02.2016 04:20, Arne Vajhøj wrote:
>
>> What is your goal? To produce easily understandable code? To save
>> a few micro seconds?
>
> That is really the first question to answer.
>
>> If the first then I think you current code is fine.
>>
>> If the second then you will need to be creative.
>>
>> Here is my attempt to be creative:
>
> I'm a bit more old fashioned, maybe that's why I do not find the
> advantage of streams so big here.  I was also considering, we might not
> know how large the initial collection is.  So the cost of iterating
> twice could be significant.  So:
[code snipped]

I also assume that iterating twice would lead to unacceptable runtime.

The excellent library jool (https://github.com/jOOQ/jOOL) has what they 
call "combining collectors". These are higher-order collectors that 
apply a number of other collectors simultaneously to a stream and return 
a tuple (in our case a pair) of results.

The collector to be applied would have to be a filtering collector. Java 
8 does not have one, but there will be such a beast in Java 9, see here: 
https://bugs.openjdk.java.net/browse/JDK-8144675

You can use these two elements to get a solution that is very similar to 
Eric's idea of abusing groupingBy:

Collector<T, ?, Tuple2<List<T>, List<T>>> filtering = Tuple.collectors(
     filtering(filter, toList()), filtering(anotherFilter, toList()));
Tuple2<List<T>,List<T>> filtered =  mycoll.stream().collect(filtering);
return !filtered.v1.isEmpty() ? filtered.v1 : filtered.v2;

It shares with Eric's idea the advantages of being parallelizable, which 
would be more difficult with Arne's and Robert's suggestions, and using 
official or well-established libraries.

-- Sebastian


PS: I append the very bare bones of the necessary parts from JDK 9 and 
the jool library (this will work, akthough the original code is a bit 
different, please look at the sources cited above for the real thing).

public static <T, A, R> Collector<T, A, R> filtering(
                               Predicate<? super T> predicate,
                               Collector<? super T, A, R> downstream) {
     return Collector.of(
                 downstream.supplier(),
                 (r, t) -> { if (predicate.test(t)) {
                               downstream.accumulator().accept(r, t);
                            }},
                 downstream.combiner(),
                 downstream.finisher());
}

public static interface Tuple {

     static <T1, T2> Tuple2<T1, T2> tuple(T1 v1, T2 v2) {
         return new Tuple2<>(v1, v2);
     }

     static <T, A1, A2, D1, D2>
         Collector<T, Tuple2<A1, A2>, Tuple2<D1, D2>>
             collectors(Collector<T, A1, D1> collector1,
                        Collector<T, A2, D2> collector2) {
                 return Collector.<T, Tuple2<A1, A2>, Tuple2<D1, D2>> of(
                     () -> tuple(
                         collector1.supplier().get(),
                         collector2.supplier().get()),
                     (a, t) -> {
                         collector1.accumulator().accept(a.v1, t);
                         collector2.accumulator().accept(a.v2, t);
                     } ,
                     (a1, a2) -> tuple(
                         collector1.combiner().apply(a1.v1, a2.v1),
                         collector2.combiner().apply(a1.v2, a2.v2)),
                     a -> tuple(
                         collector1.finisher().apply(a.v1),
                         collector2.finisher().apply(a.v2))
                );
     }
}

public static class Tuple2<T1, T2> implements Tuple {

     public final T1 v1;
     public final T2 v2;

     public Tuple2(T1 v1, T2 v2) {
         this.v1 = v1;
         this.v2 = v2;
     }
}

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.java.programmer


csiph-web