Issue
I have a stream and I would like to extract one value based on the condition :
- If any match condition A find first that match A
- Else if any match condition B find first that match B
- Else if any match condition C find first that match C
- Else null
For example :
Stream<String> stream0 = Stream.of("a", "b", "c", "d");
Stream<String> stream1 = Stream.of("b", "c", "d", "a");
Stream<String> stream2 = Stream.of("b", "c", "d", "e");
Stream<String> stream3 = Stream.of("d", "e", "f", "g");
findBestValue(stream0); //should return "a"
findBestValue(stream1); //should return "a"
findBestValue(stream2); //should return "b"
findBestValue(stream3); //should return null
I tried the following, but that return a java.lang.IllegalStateException: stream has already been operated upon or closed
private static String findBestValue(Stream<String> stream) {
return stream.filter(str -> str.equals("a"))
.findFirst()
.orElse(stream.filter(str -> str.equals("b"))
.findFirst()
.orElse(stream.filter(str -> str.equals("c"))
.findFirst()
.orElse(null))
);
}
Any idea how I could archive that ?
Note : a, b, c are just for the example I can't use .sort()
Solution
Once the stream is consumed - it's done, and you can't use it anymore. Otherwise, you would get an IllegalStateException
.
Because Stream is a mean of iteration of the source of data, not a container of data.
Here's a quote from the API documentation:
Streams differ from collections in several ways:
- No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
You can store the incoming stream data into a collection, and then interact with it. As a collection we can use a LinkedHashMap
(for the example with strings a set, would be enough, but if we generify the code the map would be useful to deal with complex objects that are contrary to string not necessarily identical even if they are equal).
That's how it might be implemented without using hard-coded conditions:
private static <T> T findBestValue(Stream<T> stream,
T key1, T key2, T key3) {
Map<T, T> map = stream.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(l, r) -> l, // preserve the first value - precaution against duplicates
LinkedHashMap::new
));
return Stream.of(map.get(key1), map.get(key2), map.get(key3))
.filter(Objects::nonNull)
.findFirst()
.orElse(null); // NOTE that it's NOT the most recommended option (we can return Optional itself or use methods like orElseThrow())
}
And we can make this method expect a varargs of keys:
private static <T> T findBestValue(Stream<T> stream,
T... keys) {
Map<T, T> map = stream.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(l, r) -> l,
LinkedHashMap::new
));
return Arrays.stream(keys)
.map(map::get)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
main()
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("a", "b", "c", "d");
Stream<String> stream2 = Stream.of("b", "c", "d", "e");
Stream<String> stream3 = Stream.of("d", "e", "f", "g");
System.out.println(findBestValue(stream1, "a", "b", "c")); //should return "a"
System.out.println(findBestValue(stream2, "a", "b", "c")); //should return "b"
System.out.println(findBestValue(stream3, "a", "b", "c")); //should return null
}
Output:
a
b
null
Answered By - Alexander Ivanchenko
Answer Checked By - Timothy Miller (JavaFixing Admin)