# Using Java 8 Streams to Find Open Ports

In the last time we saw how to use lambdas as predicates, specifically with the Java 8 [Collection#removeIf](https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-) method in order to remove elements from a map based on the predicate. In this article, we will use a predicate to filter elements from a stream, and combine it with a generator to find the first open port in a specific range.

## Use Case
The use case is a (micro)service-based environment where each new service binds to the first open port it finds in a specific port range (49152 to 65535, as [defined by IANA](http://www.iana.org/assignments/port-numbers)). We want to choose a port at random in this range and bind to that port if it is open. If it is not open, we will repeat the process until we find an open port or until we reach a pre-defined number of attempts.

## Original Pre-Java 8 Code
```java
Integer findOpenPort(int maxAttempts) {
    Integer assignedPort = null;
    int count = 0;
    while (count < maxAttempts) {
        int port = randomPort();
        if (portChecker.isAvailable(port)) {
            assignedPort = port;
            break;
        }
        count++;
    }
    return assignedPort;
}
```

### Observations
1. Returns an Integer indicating the port or `null` if none is found.
2. Utilizes mutable variables `assignedPort` and `count` to store state and track attempts.
3. Executes a loop while the maximum attempts haven’t been exceeded.
4. Uses a port checker to determine availability and breaks if a port is found. 
5. Uses a ternary expression for return value.

## Refactoring with Java 8 Stream API
To refactor using Java 8 streams:
1. Generate a sequence of random ports.
2. Filter for open ports and return the first open port encountered. 
3. Return an empty value that indicates no open port was found, represented as an `OptionalInt`.

Here is the refactored code:
```java
OptionalInt findOpenPortStream(int maxAttempts) {
    return IntStream.generate(this::randomPort)
                    .filter(portChecker::isAvailable)
                    .limit(maxAttempts)
                    .findFirst();
}
```

### Explanation
- The method returns `OptionalInt`, providing a clear indication of presence or absence of a value.
- Generates an infinite sequence of random integers and limits it to `maxAttempts`.
- The filter method uses method reference to the `isAvailable` method.
- `findFirst` effectively “short-circuits,” terminating once a valid port is found.

### Advantages of the Functional Approach
- No mutable variables involved, simplifying the logic.
- Declarative nature improves readability.
- Easier to compose with generic methods.

## Example of a Re-Usable Method
You could implement a method to find the first random number above two billion within 10 attempts:
```java
OptionalInt result = findFirst(randomSupplier, n -> n > 2000000000, 10).orElse(42);
```

### Conclusion
The functional approach allows composability and keeps the code concise, focusing on the business logic rather than the mechanism. This style can significantly improve readability and maintainability.
