Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.java.programmer > #33854 > unrolled thread
| Started by | Philipp Kraus <philipp.kraus.flashpixx@gmail.com> |
|---|---|
| First post | 2016-02-20 04:28 -0800 |
| Last post | 2016-02-22 00:13 +0100 |
| Articles | 6 — 5 participants |
Back to article view | Back to comp.lang.java.programmer
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
| From | Philipp Kraus <philipp.kraus.flashpixx@gmail.com> |
|---|---|
| Date | 2016-02-20 04:28 -0800 |
| Subject | stream 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]
| From | Eric Sosman <esosman@comcast-dot-net.invalid> |
|---|---|
| Date | 2016-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]
| From | Arne Vajhøj <arne@vajhoej.dk> |
|---|---|
| Date | 2016-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]
| From | Robert Klemme <shortcutter@googlemail.com> |
|---|---|
| Date | 2016-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]
| From | Arne Vajhøj <arne@vajhoej.dk> |
|---|---|
| Date | 2016-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]
| From | Sebastian <news@seyweiler.dyndns.org> |
|---|---|
| Date | 2016-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