Join strings in java 8

Just about every programming language natively supports a string join() function. Find out in this tutorial how to java 8 finally added support to join strings.

Detailed Video Notes

You might be accustomed to using third party libraires such as Apache Commons StringUtils or Guava String joiner to join strings. The long awaited feature was delivered in core java 8 to simplify dependencies and gain the ability to join strings. Let's walk through some common examples exploring these new features.

Project set up

[0:16]

Generating a java project from a maven archetype quick start we will update the project to use java 8 and add the dependencies of junit.

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

Using StringJoiner or String.join

[0:23]

Two new approaches to concatenate strings in java 8 is to use String.join or StringJoiner. Lets instantiate a StringJoiner class and use a space as the delimiter or the value that will be place between each string. Adding a few random strings of "LevelUp", "Lunch" and "Tutorial" we can call the toString to convert the StringJoiner to a string. Using a junit 4 assert statement to validate the expected results.

String.join is an abbreivated call in a sense but if you look under the covers it delegates to StringJoiner. Lets use the same delimiter and values to concatenate.

Lets run the test to validate our resutls.

@Test
public void basic_string_join() {

    StringJoiner stringJoiner = new StringJoiner(" ");

    String concatenatedString = stringJoiner.add("LevelUp").add("Lunch")
            .add("Tutorial").toString();

    assertEquals("LevelUp Lunch Tutorial", concatenatedString);

    String stringJoined = String.join(" ", "LevelUp", "Lunch", "Tutorial");

    assertEquals("LevelUp Lunch Tutorial", stringJoined);
}

Join ints in Instream

[0:58]

Another class added in java 8 was IntStream which is a specialized stream for dealing with primitive ints. In the instance we wanted to convert the IntStream to a string we could join all the values together using a specialized collector Collectors.joining(). This collector under the covers uses a StringJoiner that concatenates the input elements of the stream and will seperate each value with a comma and append a prefix and suffix bracket. This technique could be use for IntStream or sister streams LongStream or DoubleStream as well.

Lets run our example and validate the test is successful.

@Test
public void join_intstream_values(){

    String intStreamToString = IntStream.of(1, 2, 3, 4)
        .boxed()
        .map(String::valueOf)
        .collect(Collectors.joining(",", "[", "]"));

    assertEquals("[1,2,3,4]", intStreamToString);
}

Join list strings to CSV

[1:31]

Next, while I wouldn't suggest building your own CSV concatenator, you could take a list of strings and produce a comma seperated string. There are many open source java libraries such open csv or commons csv that are a better suited for csv parsing. In the first snippet let us use String.join passing in a comma as the delimiter and "hello", "world" and "csv" as elements then writing a unit test to validate our results. The second snippet we will create a list with the same values then using Collectors.joining to collect or join all the strings in the arraylist together.

Lets run our test to validate.

@Test
public void join_string_csv() {

    String stringToCSV = String.join(",", "hello", "world", "csv");

    assertEquals("hello,world,csv", stringToCSV);

    List<String> randomStrings = new ArrayList();
    randomStrings.add("hello");
    randomStrings.add("world");
    randomStrings.add("csv");

    String listToCSV = randomStrings.stream().collect(
            Collectors.joining(","));

    assertEquals("hello,world,csv", stringToCSV);

}

Joining object fields

[2:4]

In our next snippet we created a Team class and mocked up some data related to the NCAA tournament. Often times when you have a list of objects, you may want to pull fields across objects to produce a string. In this snippet we want to join all the team's names and seperate it with a dash.

Calling the stream.map and passing in Team::getName method reference will map each Team name. You can think of it as looping over a for each loop calling team.getName or written out for (Team team : teams) {team.getName();}. Next passing in using Collectors.joining("-") will join each element with a dash. Following screencast #29, print a stream, we could use a foreach loop to print the elements of a stream.

Again, lets run our unit test to validate the results.

class Team {

    String name;
    String state;

    public Team(String name, String state) {
        super();
        this.name = name;
        this.state = state;
    }

    public String getName() {
        return name;
    }

}

@Test
public void join_list_objects_values() {

    List<Team> teams = new ArrayList<>();
    teams.add(new Team("Badgers", "Wisconsin"));
    teams.add(new Team("Bearcats", "Ohio"));
    teams.add(new Team("Terrapins", "Maryland"));

    String allTeams = teams.stream().map(Team::getName)
            .collect(Collectors.joining("-"));

    assertEquals("Badgers-Bearcats-Terrapins", allTeams);

}

Join map elements to produce json

[2:40]

Again, not a recommended approach to producing json but this snippet shows you the power of StringJoiner prefix and suffix. Note: the output of this json is not valid. A more appropirate solution would be to use jsonarray to list using jackson or gson fromjson list to convert your object to json.

Creating a HashMap with some seed data we can call entrySet().stream() and .map(Object::toString) to call the toString method on each value which will return the "key=value". We then can collect each value using Collectors.joining specifing a comma as a delimiter and the prefix and the suffix of the result to contain "{ }".

Let's run our test to validate the results.

@Test
public void map_values_to_json() {

    Map<String, String> mapToJson = new HashMap<String, String>();
    mapToJson.put("name", "Justin");
    mapToJson.put("city", "Janesville");
    mapToJson.put("ncaa-team", "UW badgers");

    String json = mapToJson.entrySet().stream()
            .map(Object::toString)
            .collect(Collectors.joining(",", "{", "}"));

    assertEquals("{city=Janesville,name=Justin,ncaa-team=UW badgers}", json);

}

Although java 8 has made progress making it easier for developers to join strings, I don't think it is as flexible, powerful and as fluent as guava joiner quite yet.

Thanks for joining in today's level up, have a great day!