Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to replace all loop constructs in Java with stream-based constructs?

I am exploring the possibilities of the Java Stream API in order to find out if one could possibly replace all loop-based constructs with stream-based constructs. As an example that would probably hint to the assumption that this is actually possible, consider this:

Is it possible to use the Stream API to split a string containing words delimited by spaces into an array of strings like the following call of String.split(String) would do ?

String[] tokens = "Peter Paul Mary".split(" ");

I am aware of the fact that a stream-based solution could make itself use of the String.split(String) method like so:

Stream.of("Peter Paul Mary".split(" "))
      .collect(Collectors.toList());

or make use of Pattern.splitAsStream(CharSequence) (the implementation of which certainly uses a loop-based approach) but I am looking for a Stream-only solution, meaning something along the lines of this Haskell snippet:

words   :: String -> [String]
words s =  case dropWhile Char.isSpace s of
                  "" -> []
                  s' -> w : words s''
                        where (w, s'') = break Char.isSpace s'

I am asking this because I am still wondering if the introduction of the Stream API will lead to a profound change in the way we handle object collections in Java or just add another option to it, thus making it more challenging to maintain a larger codebase rather than to simplify this task in the long run.

EDIT: Although there is an accepted answer (see below) it only shows that its possible in this special case. I am still interested in any hints to the general case as required in the question.

like image 699
Angle.Bracket Avatar asked Apr 01 '26 09:04

Angle.Bracket


2 Answers

A distinct non answer here: you are asking the wrong question!

It doesn't matter if all "loop related" lines of Java code can be converted into something streamish.

Because: good programming is the ability to write code that humans can easily read and understand.

So when somebody puts up a rule that says "we only use streams from hereon for everything we do" then that rule significantly reduces your options when writing code. Instead of being able to carefully decide "should I use streams" versus "should I go with a simple old-school loop construct" you are down to "how do I get this working with streams"!

From that point of view, you should focus on coming up with "rules" that work for all people in your development team. That could mean to empasize the use of stream constructs. But you definitely want to avoid the absolutism, and leave it to each developer to write up that code that implements a given requirement in the "most readable" way. If that is possible with streams, fine. If not, don't force people to do it.

And beyond that: depending on what exactly you are doing, using streams comes also with performance cost. So even when you can find a stream solution for a problem - you have to understand its runtime cost. You surely want to avoid using streams in places where they cost too much (especially when that code is already on your "critical path" regarding performance, like: called zillions of times per second).

Finally: to a certain degree, this is a question of skills. Meaning: when you are trained in using streams, it is much easier for you to A) read "streamish" code that others wrote and B) coming up with "streamish" solutions that are in fact easy to read. In other words: this again depends on the context you are working. The other week I was educating another team on "clean code", and my last foil was "Clean Code, Java8 streams/lambdas". One guy asked me "what are streams?" No point in forcing such a community to do anything with streams tomorrow.

like image 63
GhostCat Avatar answered Apr 03 '26 22:04

GhostCat


Just for fun (this is one horrible way to do it), neither do I know if this fits your needs:

List<String> result = ",,,abc,def".codePoints()
            .boxed()
            // .parallel()
            .collect(Collector.of(
                    () -> {
                        List<StringBuilder> inner = new ArrayList<>();
                        inner.add(new StringBuilder());
                        return inner;
                    },
                    (List<StringBuilder> list, Integer character) -> {
                        StringBuilder last = list.get(list.size() - 1);
                        if (character == ',') {
                            list.add(new StringBuilder());
                        } else {
                            last.appendCodePoint(character);
                        }
                    },
                    (left, right) -> {
                        left.get(left.size() - 1).append(right.remove(0));
                        left.addAll(right);
                        return left;
                    },
                    list -> list.stream()
                            .map(StringBuilder::toString)
                            .filter(x -> !x.equals(""))
                            .collect(Collectors.toList())

    ));
like image 35
Eugene Avatar answered Apr 03 '26 22:04

Eugene



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!