Java Stream Uses
Tags: java, streams, functional, lambda
Published: 2019-03-22 15:18:43

---
title: Java Stream Uses
published: true
tags: java,streams,functional,lambda
canonical_url: https://smcrow.net/2018/06/17/java-stream-uses/
---

Introduced in Java 8, the [Java Stream API](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html) adds functional operations to collections, arrays, and other iterables. Streams are _not_ collections or data structures, but they are used to enhance existing structures.

Here are some examples on how the Stream API can be used.

## First Example: Iterate over a list of elements and print out all other elements.

This is a strange example, however it is something that I have needed on a few occasions. Let’s say you have a list of friends and you want to print out each person and their friends. Essentially, you need to operate on each element in the list that is _not_ the current element in the list.

### Without Streams

Without streams, one might approach it this way:

```java
for (String person : FRIENDS) {
    System.out.println("Friends of " + person + ":");
    for (String other : FRIENDS) {
        if (!person.equals(other)) {
            System.out.println(other);
        }
    }
}
```

If you wanted to print them in-line, the task is a little bit different:

```java
for (String person : FRIENDS) {
    List<String> friends = new ArrayList<>(FRIENDS);
    friends.remove(person);
    System.out.println("Friends of " + person + ": " + String.join(", ", friends));
}
```

This doesn’t look too messy, because we are dealing with a list of `String`s.

### With Streams

With streams, we can do the following:

```java
FRIENDS.forEach(person -> {
    System.out.println("Friends of " + person + ":");
    FRIENDS.stream().filter(other -> !person.equals(other)).forEach(System.out::println);
});
```

If you wanted to print them in-line, it would look something like this:

```java
FRIENDS.forEach(person -> {
        String friends = FRIENDS.stream().filter(other -> !person.equals(other)).collect(Collectors.joining(", "));
        System.out.println("Friends of " + person + ": " + friends);
    });
```

#### What’s going on here?

We are using the `filter` method which creates a new stream based on the given predicate (kind of like a conditional). The predicate `other -> !person.equals(other)` is instructing the new stream to not include the element in the list that matches the current element being iterated on.

In the first example, we are using the `forEach` method which takes a lambda expression and calls it to each of the elements.

In the second example, we are using the `collect` method which is a terminating method (ends the stream). By passing in `Collectors.joining`, we are telling the `collect` method that we would like a joined `String` to be returned.

## Second Example: Multiply all elements by five.

Suppose we want to operate on each of the elements in a list without mutating the original list. In this example, we are going to take a list of `Integer`s and multiply them all by five.

### Without Streams

Here’s how we might approach this problem without streams:

```java
List<Integer> multiplied = new ArrayList<>();
for (Integer number : NUMBERS) {
    multiplied.add(number * 5);
}

// To print the results you most likely used something like StringUtils.join, or Guava's Joiner
// Or you did messy things like this (not recommended because of the replacement):
String output = Arrays.toString(multiplied.toArray()).replace("[", "").replace("]", "");
System.out.println(output);
```

In the previous example, I touched on how one might want to display the information in-line. It wasn’t as difficult of a problem, because the elements we were joining were already `String`s. This is more of a challenge in the current example.

You might be familiar with [Apache Commons](https://commons.apache.org/)’s `StringUtils` or [Google Guava](https://github.com/google/guava)’s `Joiner`. As you will see, they are not necessary when using streams.

### With Streams

Here’s how me might approach this problem using the streams api:

```java
List<Integer> multiplied = NUMBERS.stream().map(i -> i * 5).collect(Collectors.toList());

String output = multiplied.stream().map(Object::toString).collect(Collectors.joining(", "));
System.out.println(output);
```

#### What’s going on here?

This time we’re using the `map` function which is similar to `forEach` in that it calls a lambda on each of the elements of the stream. However, there is an _important_ key difference:

- `forEach` calls a lambda on each of the elements.
- `map` calls a lambda on each of the elements and returns a _new_ stream of the results.

This means that when using `map` we can mutate the elements and copy the results into a new `List`. We use it both to apply the lambda `i -> i * 5` which multiples each element by 5, and to apply the lambda `Object::toString` to convert each `Integer` to a string for joining.

We are also using the `collect` method with `Collectors.toList()` which instructs `collect` to create a `List` and terminate the stream.

## Third Example: Create lists of properties from a list of elements.

Suppose we have a list of people with the name and age property. Something like this:

```java
private static final List<Person> PEOPLE = List.of(new Person("Sarah", 29),
                                                    new Person("John", 16),
                                                    new Person("Mary", 21),
                                                    new Person("Susan", 55));

private static class Person {
    private String name;
    private int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    String getName() {
        return name;
    }

    int getAge() {
        return age;
    }
}
```

What if we wanted to create two separate lists? One list will contain the names, and another will contain the ages.

### Without Streams

Without streams, this might look something like:

```java
List<String> names = new ArrayList<>();
List<Integer> ages = new ArrayList<>();

for (Person person : PEOPLE) {
    names.add(person.getName());
    ages.add(person.getAge());
}

// To print the results you most likely used something like StringUtils.join, or Guava's Joiner
// Or you did messy things like this:
System.out.println("Names: " + String.join(", ", names.toArray(new String[0])));
System.out.println("Ages: " + Arrays.toString(ages.toArray()).replace("[", "").replace("]", ""));
```

### With Streams

With streams, we can do the following:

```java
List<String> names = PEOPLE.stream().map(Person::getName).collect(Collectors.toList());
List<Integer> ages = PEOPLE.stream().map(Person::getAge).collect(Collectors.toList());

System.out.println("Names: " + names.stream().collect(Collectors.joining(", ")));
System.out.println("Ages: " + ages.stream().map(Object::toString).collect(Collectors.joining(", ")));
```

#### What’s going on here?

This is really just a summary of what we’ve discussed in the previous examples. We are taking advantage of the fact that `map` creates a new stream and are calling the `getName` and `getAge` property on each person.

## Conclusion

Hopefully this shares some of the value of using streams in your code. I still feel like they’re a bit clunky compared to some of the offerings in JavaScript or Kotlin. However, I also think it’s a step in the right direction.

Full code for each example can be found on GitHub at [cr0wst/java-sandbox](https://github.com/cr0wst/java-sandbox/tree/master/src/main/java/net/smcrow/sandbox/streams)