IntStream to String and Finding the First Element that Matches a Predicate


IntStream to String

Java does not have a Char Stream, so when working with Strings and constructing a Stream of Characters, an option is to get a IntStream of code points using String.codePoints() method. So IntStream can be obtained as below:

public IntStream stringToIntStream(String in) {
    return in.codePoints();
}

It is a bit more involved to do the conversion other way around i.e. IntStreamToString. That can be done as follows:

public String intStreamToString(IntStream intStream) {
	return intStream.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
}

Finding the First Element that Matches a Predicate

It is possible to find the first element of a Stream that matches a condition.

For this example, we will find the first Integer whose square is over 50000.

IntStream.iterate(1, i -> i + 1) // Generate an infinite stream 1,2,3,4...
        .filter(i -> (i*i) > 50000) // Filter to find elements where the square is >50000
        .findFirst(); // Find the first filtered element

This expression will return an OptionalInt with the result.

Note that with an infinite Stream, Java will keep checking each element until it finds a result. With a finite Stream, if Java runs out of elements but still can't find a result, it returns an empty OptionalInt.


Using Streams and Method References to Write Self-Documenting Processes

Method references make excellent self-documenting code, and using method references with Streams makes complicated processes simple to read and understand. Consider the following code:

public interface Ordered {
	default int getOrder(){
		return 0;
	}
}
 
public interface Valued<V extends Ordered> {
	boolean hasPropertyTwo();
	V getValue();
}
 
public interface Thing<V extends Ordered> {
	boolean hasPropertyOne();
	Valued<V> getValuedProperty();
}
 
public <V extends Ordered> List<V> myMethod(List<Thing<V>> things) {
	List<V> results = new ArrayList<V>();
	for (Thing<V> thing : things) {
		if (thing.hasPropertyOne()) {
			Valued<V> valued = thing.getValuedProperty();
			if (valued != null && valued.hasPropertyTwo()){
				V value = valued.getValue();
				if (value != null){
					results.add(value);
				}
			}
		}
	}
	results.sort((a, b)->{
		return Integer.compare(a.getOrder(), b.getOrder());
	});
	return results;
}

This last method rewritten using Streams and method references is much more legible and each step of the process is quickly and easily understood - it's not just shorter, it also shows at a glance which interfaces and classes are responsible for the code in each step:

public <V extends Ordered> List<V> myMethod(List<Thing<V>> things) {
	return things.stream()
		.filter(Thing::hasPropertyOne)
		.map(Thing::getValuedProperty)
		.filter(Objects::nonNull)
		.filter(Valued::hasPropertyTwo)
		.map(Valued::getValue)
		.filter(Objects::nonNull)
		.sorted(Comparator.comparing(Ordered::getOrder))
		.collect(Collectors.toList());
}

Converting a Stream of Optional to a Stream of Values

You may need to convert a Stream emitting Optional to a Stream of values, emitting only values from existing Optional. (ie: without null value and not dealing with Optional.empty()).

Optional<String> op1 = Optional.empty();
Optional<String> op2 = Optional.of("Hello World");
 
List<String> result = Stream.of(op1, op2)
	.filter(Optional::isPresent)
	.map(Optional::get)
	.collect(Collectors.toList());
 
System.out.println(result); //[Hello World]

Get a Slice of a Stream

Example : Get a Stream of 30 elements, containing 21st to 50th (inclusive) element of a collection.

final long n = 20L; // the number of elements to skip
final long maxSize = 30L; // the number of elements the stream should be limited to
final Stream<T> slice = collection.stream().skip(n).limit(maxSize);

Notes:

  • IllegalArgumentException is thrown if n is negative or maxSize is negative
  • both skip(long) and limit(long) are intermediate operations
  • if a stream contains fewer than n elements then skip(n) returns an empty stream
  • both skip(long) and limit(long) are cheap operations on sequential stream pipelines, but can be quite expensive on ordered parallel pipelines

Basic Programs