Navigating the uncharted Stream
Tags: java, learning, functional
Published: 2017-11-07 09:33:14

---
title: "Navigating the uncharted Stream"
published: true
description: As someone who neglected Java for a long time, it has been difficult to assimilate all the changes made since. I've decided to illustrate this struggle with my learning of the Stream object.
tags: java, learning, functional
cover_image: https://upload.wikimedia.org/wikipedia/commons/8/88/Split_Rock_River%2C_Two_Harbors%2C_Minnesota_%286968301759%29.jpg
---

# Wrong bend of the river

When I left school, Java 6 had just been released and wasn’t really integrated into the school curriculums yet. But I was content to think that I could simply stay as a web developer and forget all that Java is (didn't pay that much attention), forget about IDE, compiler, and all that nonsense. Who needs something like that when you’ve got your trusty text editor to power your web wizardry? Boy, was I wrong.

Since about 2 years, I need to use Java again, I first went in it with my basic programming language knowledge, then rediscovered Object-Oriented Programming and lately I’ve been introduced to Java 8 features. But each time I encountered a new obstacle in my way, I used the new or re-acquainted knowledge more as a Band-Aid, leading more often degraded a bit the quality of the code. Lately, after some refactoring session to level things a bit, I realized that I needed to change strategy. So instead of always scratch the surface, I decided to dig deeper.
<a id="unchartedStream" />
# The uncharted Stream

It first began when I wanted to find specific information in files, to see if the content wasn’t affected during the course of a test. I would have used `reader = new BufferedReader(new FileReader(file));` to check each line for the specific pattern, And this for each file, if I didn't find this question on how to [find pattern in files][1] which used the Stream object introduced in Java 8. As a result, I came up with the following code:

```Java
// Searching for <filename:"…"> and extract its value.
Pattern pattern = Pattern.compile("filename:\"(.+)\"");

// List of objects containing information on the files
// generated at the beginning of the test.
List<GeneratedFile> generatedFileList = myTest.getFileList();

For (File file : fileList){
    Optional<String> message = Files.lines(Paths.get(file.toURI()))
                                    .filter(pattern.asPredicate())
                                    .findFirst();
    if (message.isPresent())
    {
        String fileContent = message.get();
        Matcher matcher = pattern.matcher(fileContent);
        matcher.find();
        String value = matcher.group(1);
        GeneratedFile generatedFile = findFileByOriginalName(value, generatedFileList);
        if(generatedFileList == null){
            Assert.fail("File "
                        + originalFilename
                        + " wasn't found.");
        }
    }
}

private static GeneratedFile findFileByOriginalName(String filename,
                                List<GeneratedFile> generatedFileList){
    for (GeneratedFile generatedFile : generatedFileList){
        if(generatedFile.getFileName().equals(filename)){
            return generatedFile;
        }
    }
    return null;
}

```
It became more complicated when I wanted to extract another data point from the file content and it threw me an `IllegalStateException`. I then discovered the `Supplier` which would allow me to get the content of the file for each search without worrying about the Stream auto-closing:

```Java
Supplier<Stream<String>> linesSupplier = null;
try
{
    linesSupplier = () -> getFileStream(tempfile);
}
catch (Exception e)
{
    log.error(e);
}

//Replace the pattern for each piece of information searched.
Optional<String> message = linesSupplier.get()
                                        .filter(filenamePattern.asPredicate())
                                        .findFirst();

private static Stream<String> getFileStream(File file)
{
    try
    {
        return Files.lines(Paths.get(file.toURI()));
    }
    catch (IOException e)
    {
    log.error("Exception while trying to supply file "
              + file.getName()
              + " content"
              , e);
    }
    return null;
}
```

# Rocky ride

I was quite intrigued and decided to use my newfound knowledge everywhere I thought applicable. And I thought it would be perfect for dealing with any file operation and get rid of those pesky `finally` clause. I thought that the new and shiny thing would be so much better. Turns out that a `Stream` is [not always auto-closeable][2], and furthermore it completely broke my code and became an unreadable mess...

Needless to say, my understanding of `Stream` was not as good as the one I had for `Optional` for which I already found [a use I mostly agree upon][3]. At that point, I decided that I should thrive to better understand what I use, so I could make the best of it.

So when does one use `Stream`? Let me quote an answer to [this question][4] as I asked on Stack Overflow:

> If you can easily separate out distinct steps to apply to each dataset member, a Stream seems like a solid bet since that API can combine steps whenever possible.

It means that you need to have a concrete idea of what needs to happen to a point where you can describe it with discrete steps.

Furthermore, I found out that one should be aware of the order in which you do your operations: first, you need to filter, then manipulate and finally collect. Even if it seems like it wouldn't matter most of the time, there are [some cases where it could be relevant][5] and it makes more sense from a story standpoint.
<a id="crashingWaves"/>
# Crashing waves

Let's talk about a concrete example, the one I alluded at the beginning of the previous section.

I have a template file that I want to populate by replacing placeholder strings with the wanted values. For that, I feed my method two lists, one with the placeholder keys and one with the desired values.

```Java
private static void populateTemplateFile(ArrayList<String> sKeys, ArrayList<String> sVals){
    String line="";
    String newline = System.getProperty("line.separator");
    BufferedReader reader = null;
    FileWriter writer = null;
    StringBuilder storeLine = new StringBuilder("");

    try{
        File file = new File(TEMPLATE_FILE);
        reader = new BufferedReader(new FileReader(file));
        while((line = reader.readLine()) != null){
            storeLineBuilder.append(line + newline);
        }
        reader.close();

        String storeLine = storeLineBuilder.toString();
        for (int iCtr=0; iCtr<sKeys.size();iCtr++){
            storeLine=storeLine.replaceAll(sKeys.get(iCtr), sVals.get(iCtr));
        }

        writer = new FileWriter(TARGET_FILE);
        writer.write(storeLine);
        writer.close();

    } catch(FileNotFoundException e) {
        log.error("The file " + TARGET_FILE + " could not be found!", e);
    } finally {
        if(reader != null) {
            reader.close();
        }
        if(writer != null) {
            writer.close();
        }
    }
}
```

Now it was my first attempt at adapting the method to use `Stream`:

```Java
private static void populateTemplateFile(ArrayList<String> sKeys, ArrayList<String> sVals) {
    File file = new File(TEMPLATE_FILE);
    File trgFile = new File(TARGET_FILE);
    Stream<String> lines = null;
            
    try
    {
        lines = Files.lines(Paths.get(file.toURI()));

        //Newline for readability, this is only one line.
        Stream<String> replaced = lines
            .map(line -> {
                for(int iCtr=0; iCtr<sKeys.size(); iCtr++)
                    line.replaceAll(sKeys.get(iCtr), sVals.get(iCtr));
                    return line;});
        lines.close();
        Files.write(Paths.get(trgFile.toURI()), replaced.toString().getBytes());
    }
    catch (IOException e)
    {
       log.error("Exception while trying to create " + TARGET_FILE, e);
    }
    finally {
        lines.close();
    }
}
```

What a mess did I make... At this point, I would have expected a three-beat story of filtering, processing, collecting but here is what I have:

1. Get the stream I initialized earlier.
2. For each of the key, replace it by its corresponding value. Repeat for each line.
3. Profit?

It appears quite clunky to me for two reasons: there are no clear collecting steps and we have here a double iteration and after further researching the subject, it appears that the use of Stream, in most cases, would not be advised.

In the end I couldn't find a process which would do things better as what is currently used.

# Parting thoughts

I am still at the beginning of my journey, but now I have a much better insight on when to use `Stream`. To summarize:

- There should be a clear story as to what you want to do.
- The story should, in general, follow the pattern filter -> process -> collect.
- Double loop is a no go.

In the end, I now have the feeling that I acquired a comprehension of the utility of `Stream` and will be able to use it better from now on.

Until the next bend of the river.

[1]: https://stackoverflow.com/questions/34791138/find-pattern-in-files-with-java-8
[2]: https://stackoverflow.com/questions/22125169/why-is-java-8-stream-class-autocloseable
[3]: https://blog.codefx.org/techniques/intention-revealing-code-java-8-optional/
[4]: https://stackoverflow.com/questions/46951279/are-non-parallel-streams-meant-to-do-an-operation-in-mass-on-big-amount-of-data
[5]: https://blog.jooq.org/2017/07/03/are-java-8-streams-truly-lazy-not-completely/