Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decision between using a Stream or a Loop

By writing applications in Java there are many use cases for java.util.Collection. Since java.util.stream.Stream was introduced with Java 8, I came over some use-cases where it is difficult to decide what to use.

For example: You are going to write some util-methods.

public static List<?> filterHashToList(int hash, Collection<?> toFilter) {
    return toFilter.stream()
        .filter((Object o) -> hash == o.hashCode())
        .collect(Collectors.toCollection(LinkedList::new));
}

What about writing it like this:

public static List<?> filterHashToList(int hash, Collection<?> toFilter) {
    List<Object> result = new LinkedList<>();

    for(Object o : toFilter) {
        if(hash == o.hashCode()) {
            result.add(o);
        }
    }

    return result;
}

Both methods would produce the same result. java.util.stream.Stream and java.util.stream.Collector are interfaces, so the implementation can vary as well if I use custom streams and collectors.

I think there are loads of implementations out there using the old-fashoined loop-way.

So, is it possible to answer what to use, stream or loop, by use-case? And if so, do all implementations have to be updated where appropriate?

Or should I even provide both ways by implementing util-methods? Or should I also provide a mthed returning the stream after the filtering process so you can work with that one too if required?

like image 912
narranoid Avatar asked Sep 19 '15 16:09

narranoid


People also ask

Should I use streams or for loops?

Performance: A for loop through an array is extremely lightweight both in terms of heap and CPU usage. If raw speed and memory thriftiness is a priority, using a stream is worse. Familiarity .The world is full of experienced procedural programmers, from many language backgrounds, for whom loops are familiar and streams are novel.

What is the difference between for loops and parallel streams?

There are many opinions about which style performs better. The short version basically is, if you have a small list; for loops perform better, if you have a huge list; a parallel stream will perform better. And since parallel streams have quite a bit of overhead, it is not advised to use these unless you are sure it is worth the overhead.

How to define a method that works with a stream?

You can define methods working with streams: taking them as parameters, returning them, etc. You can't define a method which takes a loop as a parameter. This allows a complicated stream operation once and using it many times.

What is the use of stream () foreach () method?

Only used to iterate collections: The stream ().forEach () is only used for accessing the collections like set and list. It can also be used for accessing arrays. The return statement within the loop doesn’t work. The same functionality of getting the 2nd element cannot be done in the same forEach () method.


2 Answers

In the absence of a posted answer I will quote Brian Goetz who echoes my sentiment and I suspect many others'.

There's nothing magic about either streams or loops. You should write the code that is most readable, clear, and maintainable. Either of these are acceptable.

like image 153
2 revs Avatar answered Sep 28 '22 10:09

2 revs


Note that in your implementation you stick with specific resulting collection type LinkedList which usually has very poor performance compared to ArrayList. What if the user of your method wants to use the resulting list in random-access manner? Probably the user of this method needs an array, because it should be passed to another API method which accepts an array. Sometimes the user just need to know how many objects with given hashCode are present in the input collection, thus there's no need to create a resulting list at all. The Java-8 way is to return streams from the methods, not the collections and let the caller decide how to collect it:

public static <T> Stream<T> filterHashToList(int hash, Collection<T> toFilter) {
    return toFilter.stream()
        .filter(o -> hash == o.hashCode());
}

And use it:

filterHashToList(42, input).count();

Or

filterHashToList(42, input).collect(toCollection(ArrayList::new));

Or

filterHashToList(42, input).toArray();

This way the method becomes very simple, so you probably don't need it at all, but if you want to do more sofisticated filtering/transformation, it's ok.

So if you don't want to change the API and still return the LinkedList, there's no need to change the implementation. But if you want to take the advantage from using Stream API, it's better to change the return type to Stream.

like image 28
Tagir Valeev Avatar answered Sep 28 '22 09:09

Tagir Valeev