Sometimes, all you need to do is go "old school" and just concatenate a bunch of strings. You can easily do that with Java Streams and Collectors.join().

In this guide, I'll make you happy by demonstrating how to do just that.

I'll even use something like a "real world" dataset so you can get an idea of what the whole process looks like when you're doing this kind of thing for a business application.

So let's get started.

The CRM App

Let's say you're building a CRM app. It does the normal stuff that CRM apps do: tracks activities between sales reps and contacts.

Those activities get stored in a MongoDB database as documents. Each document persists info like the title of the activity as well as the type, outcome, location, start time, end time, notes, and the contact involved.

As it stands right now, if you retrieve all the documents from the activities collection, the resulting data set looks like the JSON dump at this link.

As you can see, we're not messing around here. You're going to be working with real-world data.

On the Java side, the Activity class with its related classes mimic the data set that you see above. You can see examples of those classes over on GitHub.

So you can just do a findAll() on that collection above and get a List of Java objects that represent that JSON output. Then, you can use a Java Stream to filter, find, and map as you see fit.

That's what you'll do in this guide.

And, yes, you could do that kind of stuff with MongoDB aggregations. But you're not here to learn about aggregations are you?

The Simple Requirement

A list of contact names.

That's it. That's all you need to deliver for this requirement.

Fortunately, contact names are stored in denormalized fashion with activity documents in the MongoDB collection. So all you need to do is extract the contact names, grab a distinct set, and concatenate them.

Then it's winner, winner, chicken dinner.

Oh, yeah. You're delivering the contact names to a portal that will print them out on a screen. So you need to put each name on its own line.

That should be fairly easy as well.

Don't Stream It's Over

So whatcha gotta do for this req is something like this:

List<Activity> activities = activityRepo.findAll();

String names = activities
               .stream()
               .map(Activity::getContact)
               .map(contact -> contact.getLastName() + ", " + contact.getFirstName())
               .distinct()
               .collect(Collectors.joining("\n"));

System.err.println(names);

That first line gets all activity documents from the MongoDB collection. Then it translates them to Java List of  Activity objects so you can more easily play with them in your IDE.

Then, it converts the List object to a Stream object. That happens with the stream() method you see above.

Next, the code invokes the map() method to convert the Activity object to a Contact object.

Why? Because the code needs to get contact names. It doesn't care about the outlying related Activity info.

It also doesn't care about any other Contact info besides the first and last name. So the code uses another map() invocation to translate the whole Contact object into a String object showing the contact's first and last name.

Keep in mind, though: the name is formatted as last name first.

After that second map() call, the code now has a Stream with a sequence of String objects representing the names of all the contacts who've recorded activities. 

There's a problem, though. That sequence could (and does) contain duplicate names. You don't need duplicate names.

Fortunately, that distinct() method exists to remove duplicate entries in the sequence.

Finally, the code invokes collect() that uses a Collector to finalize the operation. 

You'll often see Collectors.toList() as the Collector of choice in this kind of a situation. But you're not here to learn about Collectors.toList() are you?

In this case, the code uses Collectors.joining() to concatenate all the contact names together.

But that would look messy on the screen. So the code uses the overloaded join() method to include a CharSequence that separates each contact name.

That CharSequence is nothing more than a linefeed ("\n"). That's how each name gets printed out on a new line.

And that's it. Now you're ready to test it out.

Looking Good?

You might have noticed that the code block above ends with System.err statement. That will print out all the contact names as returned from the Stream operations.

So if you run that code above with the dataset that I referenced earlier, you should get this output:

Rover, Governor
Cheng, Lucy
Joy, Yeezu
Lezilia, Frum
Mei, Opus
Simmz, Bert
Scene, Blinky
Windsor, Mercy

Beautiful. All the contact names.

But they're not sorted are they? It might be a good idea to present the names in alphabetical order.

Easy to do. Just add the sorted() method after disctinct() in the code above. Then rerun it.

I'll have an article on that shortly.

Wrapping It Up

There it is. Now you know how to concatenate strings within a stream. 

Over to you. Take what you've learned here to the next level. Use more complex joins. Concatenate something other than than contact names.

But whatever you do, just make sure you have fun!

Photo by Gabby K from Pexels