Parsing a file using Streams

Vijayakumar
4 min readJan 17, 2019

--

Basic stream flow

Table of Contents

Streams — Basic Intro

Streams — as this contemporary way of programming in Java takes stride, there are a lot of articles that cover the basic use cases and show them how it is implemented. This article takes a step further and shows how a file can be read into a custom POJO using Streams

There are traditional methods of using either ByteArrayStreams or CharacterStreams in java and read the file line by line and convert that to a POJO. This article doesn’t compare the performance of the legacy implementation and the new one, but rather focus on how to be done using Java 8 Streams.

Streams are analogous to the river streams where the data flows continuously and should be consumed at real time. So once the stream has been consumed, the data that was available previously will no longer be there.

Streams — Operators

Streams have Intermediate and Terminal operators.

Intermediate operators operate on the stream and produce a stream again, either with the modified or filtered or no data at all. These are some of the intermediate operations

a) map

b) filter

c) flatMap

This articles explains the basics of streams along with the operations

Terminal operators are the ones, that end the streams. So at the end of the stream, you would want to make sure that whatever modifications that were done to the data using the intermediate operators should be captured before ending the stream. These are the terminal operations

a) allMatch

b) anyMatch

c) noneMatch

d) foreach

e) collect

f) min

g) max

h) avg

The one, that we are currently going to look at , is collect. Now we have the default implementations to collect the data using collectors.to -List()/Map(),etc or by a custom collector class which collects the stream to our very own POJO.

Custom collector

We are going to take a look at how the custom collector is implemented

Every collector class should have its own Implementation of Supplier, Consumer, Operator and Function interfaces.

All these are function interfaces. To know more about functional interfaces, please refer the link below

Now if you are wondering what these are, then here are the basic details about them

Supplier — Its a contractual interface which guarantees that an output of any type will be produced without having an input. It has a single method, so it can act as function interfaces in streams

Consumer — Its a contractual interface which produces an output of any Type but accepts an input of any Type. In short this interfaces guarantees that we can convert one type of input to another type and give it as an output. It can be a BiConsumer which means that it accepts two inputs(may be of different Types) but produces an output of singleType

Operator — This is another functional interface that combines two inputs into one and returns that combined output

Function — Again this needs to be implemented to make the JVM aware what sort of output we are expecting from the previously modified data using intermediate operations

Finally, there are a set of characteristics that can be attributed to each custom Collector. There are quite a few — CONCURRENCY, UNORDERED and IDENTITY_FINISH. We are going to set the character as IDENTITY_FINISH

This is from the java documentation of IDENTITY_FINISH

/*** Indicates that the finisher function is the identity function and* can be elided.  If set, it must be the case that an unchecked cast* from A to R will succeed.*/

Now enough with the intro, here are the implementations

The Custom Combiner

The Custom Consumer

The Custom Supplier

The Custom Collector

Here is the piece of code to collect it using Custom implementation where a file input is transformed to a POJO

BONUS — Output a file via SpringRestAPI

Hint: To be able to understand streams and its flow, marble diagrams play a significant part. Here is the link to understand the marble diagrams

Hope this helps!! Happy coding!!

--

--