Java 8 - Stream vs Loop

So I’ve been wondering what offers best performance:

Note: items are rarely added/removed from this list after initialization.

list.stream().anyMatch((i) -> (i.check()));

or:


for (Object i : list) {
    if (i.check()) {
        return true;
    }
}
return false;

The only way to know for sure is to measure. (preferably in real usage, not microbenchmarks)

Theoretically the JIT can optimize both into

int size = list.size();
for(int i = 0; i < size; i++)
    if (/*No bounds check*/list.get(i).check()) 
        return true;
return false;

and yield the same performance, but the former is definitely farther abstracted and thus less likely to make it all the way down.

What is the context? If these are called only once, then any suboptimalities will be dwarfed by the work done on the list.

It’s for my game’s input handling. So it will be registering countless times a frame.

Possible algorithmic improvement instead:

You say that items are rarely added or removed, but are they modified often while in the list?
You could cache the truthiness of anyMatch and update it accordingly upon modification instead of computing it every query.

EDIT: since you’re asking this question, I’m assuming you’ve already determined this is a performance problem. Say yes. :point:

Interesting that you say that. That’s what I do for my entity hierarchy.

However, that will not work in this case.

So I’m guessing ultimately in this case both are equals?

Well both run just fine. But I’m trying to get used to using lambdas.

But I’m not aware of possible downsides yet. Like in this case.

Excuse the double. Didn’t see @BurntPizza 's post

This is worse than


for(int i = 0; i < list.size(); i++){
    if(list.get(i).check()){
        return true;
    }
}
return false;

It generates an Iterator instance and uses it to loop over the list, which means overhead and garbage.

The lambda version most likely does the same.

Yeah that’s the conclusion I’m reaching.

Thanks guys!

Only if the object allocation isn’t elided. It’s quite possible to reduce the iterator to simply an index in a register (the same as the c-for loop). Unfortunately you can’t really rely on it.

I’m just going to keep the lambda version for now and profile later down the road.

I got what I was looking for [:

The lambda/stream combo is especially good for ease of parallelization: [icode]list.parallelStream().anyMatch(i -> i.check());[/icode] which may also yield improvement if your list is large enough.

I ran across that in my googling journey. I’m going to give that a look in the future.

I doubt the list will get large enough to need it.

I’d say go with whatever is easiest to understand what it’s doing when you look at it. I confess that though I love lambas and use them willy nilly all over the place now that the lambda version hides much of its intent behind API whereas the explicit version shows you clearly what it is doing exactly.

Cas :slight_smile: