`Streams.concat` functions as expected when its arguments are finite streams.
If we concat two streams: the first is finite and the second is infinite, then obviously we get an infinite stream. But we can iterate over a limited number of elements. This is the scenario where the program hangs.

```java
import com.google.common.collect.Streams;

import java.util.PrimitiveIterator;
import java.util.stream.IntStream;

public class MyMain {
    public static void main(String[] args) {

        IntStream stream123 = IntStream.of(1, 2, 3);
        IntStream stream4567 = IntStream.of(4, 5, 6, 7);
        IntStream stream1234567 = Streams.concat(stream123, stream4567);
        printFirstElements(stream1234567, 5);
        // output:
        // 1
        // 2
        // 3
        // 4
        // 5



        IntStream streamInfinite99 = IntStream.generate(() -> 99);
        printFirstElements(streamInfinite99, 3);
        // output:
        // 99
        // 99
        // 99

        IntStream stream123AndThenInfinite77 = Streams.concat(IntStream.of(1, 2, 3), IntStream.generate(() -> 77));
        printFirstElements(stream123AndThenInfinite77, 5);
        // output:
        // 1
        // 2
        // 3
        // and then program hangs
    }

    static void printFirstElements(IntStream stream, int count) {
        int counter = 0;
        for (PrimitiveIterator.OfInt iterator = stream.iterator(); iterator.hasNext(); ) {
            Integer next = iterator.next();
            System.out.println(next);
            counter++;
            if (counter == count) {
                break;
            }
        }
    }
}
```
+ (delegate == null ? "<supplier that returned " + value + ">" : delegate)
          + ")";
    }
  }
  /**
   * Returns a supplier that caches the instance supplied by the delegate and removes the cached
   * value after the specified time has passed. Subsequent calls to {@code get()} return the cached
   * value if the expiration time has not passed. After the expiration time, a new value is
   * retrieved, cached, and returned. See: <a
   * href="http:en.wikipedia.org/wiki/Memoization">memoization</a>
   *
   * <p>The returned supplier is thread-safe. The supplier's serialized form does not contain the
   * cached value, which will be recalculated when {@code get()} is called on the reserialized
   * instance. The actual memoization does not happen when the underlying delegate throws an
   * exception.
   *
   * <p>When the underlying delegate throws an exception then this memoizing supplier will keep
   * delegating calls until it returns valid data.
   *
   * @param duration the length of time after a value is created that it should stop being returned
   *     by subsequent {@code get()} calls
   * @param unit the unit that {@code duration} is expressed in
   * @throws IllegalArgumentException if {@code duration} is not positive
   * @since 2.0
   */
  public static <T> Supplier<T> memoizeWithExpiration(
      Supplier<T> delegate, long duration, TimeUnit unit) {
    return new ExpiringMemoizingSupplier<T>(delegate, duration, unit);
  }
  @VisibleForTesting