Creating Predicates on the fly - java

I have a String[] of user input and I want to filter a Collection of devices based on if the hostName of the device contains any of the user input.
I'm trying to follow the lesson https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html to do this.
interface PredicateOperation{
Predicate operation(String[] input);
}
public Predicate getPredicate(String[] input, PredicateOperation op){
return op.operation(input);
}
private TufinDeviceCollection<TufinDevice> filter(TufinDeviceCollection<TufinDevice> devices) {
//Check if any HostNames of the devices contain any of the items in String[] modelContains
devices = devices.stream()
.sequential()
.filter(//How do i create this predicate?)//we need to create the lamda expression to evaulate if the hostName of device d contains any of the items String[] userInput
.collect(Collectors.toCollection(TufinDeviceCollection<TufinDevice>::new));
}
It is unclear to me how to define the PredicateOperation that goes in .filter(..)

.filter(device -> Arrays.stream(userInput)
.anyMatch(input -> device.getHostName().contains(input)))
But you need String[] userInput to be accessible from the filter method.
I guess it was an attempt to write own #FunctionalInterface to replace the standard Predicate<T>.
interface PredicateOperation {
Predicate operation(String[] input);
}
It's not very practical, though.
PredicateOperation operation = (String[] input) -> ((Object o) -> true);
Why would I need to return a Predicate if I can return a result? A little enhanced version would be
interface PredicateOperation {
boolean operation(String[] input);
}
and
PredicateOperation operation = (String[] input) -> true;
which still isn't particularly useful for Stream API since Stream#filter expects a java.util.function.Predicate<T>, not your type.
And, yes, stop using raw Predicates.

device -> Stream.of(inputs).anyMatch(input -> device.hostName.contains(input))

I prefer to separate predicate on a separate line and apply it later on filter() for readability and re-usability purposes. So the code would be somewhat like :
private TufinDeviceCollection<TufinDevice> filter(TufinDeviceCollection<TufinDevice> devices) {
Predicate< ? super TufinDevice> deviceFilter = device -> Arrays.stream(userInput)
.anyMatch(input -> device.getHostName().contains(input));
devices = devices.stream()
.sequential()
.filter(deviceFilter)
.collect(Collectors.toCollection(TufinDeviceCollection<TufinDevice>::new));
}

Related

How to set a value to variable based on multiple conditions using Java Streams API?

I couldn't wrap my head around writing the below condition using Java Streams. Let's assume that I have a list of elements from the periodic table. I've to write a method that returns a String by checking whether the list has Silicon or Radium or Both. If it has only Silicon, method has to return Silicon. If it has only Radium, method has to return Radium. If it has both, method has to return Both. If none of them are available, method returns "" (default value).
Currently, the code that I've written is below.
String resolve(List<Element> elements) {
AtomicReference<String> value = new AtomicReference<>("");
elements.stream()
.map(Element::getName)
.forEach(name -> {
if (name.equalsIgnoreCase("RADIUM")) {
if (value.get().equals("")) {
value.set("RADIUM");
} else {
value.set("BOTH");
}
} else if (name.equalsIgnoreCase("SILICON")) {
if (value.get().equals("")) {
value.set("SILICON");
} else {
value.set("BOTH");
}
}
});
return value.get();
}
I understand the code looks messier and looks more imperative than functional. But I don't know how to write it in a better manner using streams. I've also considered the possibility of going through the list couple of times to filter elements Silicon and Radium and finalizing based on that. But it doesn't seem efficient going through a list twice.
NOTE : I also understand that this could be written in an imperative manner rather than complicating with streams and atomic variables. I just want to know how to write the same logic using streams.
Please share your suggestions on better ways to achieve the same goal using Java Streams.
It could be done with Stream IPA in a single statement and without multiline lambdas, nested conditions and impure function that changes the state outside the lambda.
My approach is to introduce an enum which elements correspond to all possible outcomes with its constants EMPTY, SILICON, RADIUM, BOTH.
All the return values apart from empty string can be obtained by invoking the method name() derived from the java.lang.Enum. And only to caver the case with empty string, I've added getName() method.
Note that since Java 16 enums can be declared locally inside a method.
The logic of the stream pipeline is the following:
stream elements turns into a stream of string;
gets filtered and transformed into a stream of enum constants;
reduction is done on the enum members;
optional of enum turs into an optional of string.
Implementation can look like this:
public static String resolve(List<Element> elements) {
return elements.stream()
.map(Element::getName)
.map(String::toUpperCase)
.filter(str -> str.equals("SILICON") || str.equals("RADIUM"))
.map(Elements::valueOf)
.reduce((result, next) -> result == Elements.BOTH || result != next ? Elements.BOTH : next)
.map(Elements::getName)
.orElse("");
}
enum
enum Elements {EMPTY, SILICON, RADIUM, BOTH;
String getName() {
return this == EMPTY ? "" : name(); // note name() declared in the java.lang.Enum as final and can't be overridden
}
}
main
public static void main(String[] args) {
System.out.println(resolve(List.of(new Element("Silicon"), new Element("Lithium"))));
System.out.println(resolve(List.of(new Element("Silicon"), new Element("Radium"))));
System.out.println(resolve(List.of(new Element("Ferrum"), new Element("Oxygen"), new Element("Aurum")))
.isEmpty() + " - no target elements"); // output is an empty string
}
output
SILICON
BOTH
true - no target elements
Note:
Although with streams you can produce the result in O(n) time iterative approach might be better for this task. Think about it this way: if you have a list of 10.000 elements in the list and it starts with "SILICON" and "RADIUM". You could easily break the loop and return "BOTH".
Stateful operations in the streams has to be avoided according to the documentation, also to understand why javadoc warns against stateful streams you might take a look at this question. If you want to play around with AtomicReference it's totally fine, just keep in mind that this approach is not considered to be good practice.
I guess if I had implemented such a method with streams, the overall logic would be the same as above, but without utilizing an enum. Since only a single object is needed it's a reduction, so I'll apply reduce() on a stream of strings, extract the reduction logic with all the conditions to a separate method. Normally, lambdas have to be well-readable one-liners.
Collect the strings to a unique set. Then check containment in constant time.
Set<String> names = elements.stream().map(Element::getName).map(String::toLowerCase).collect(toSet());
boolean hasSilicon = names.contains("silicon");
boolean hasRadium = names.contains("radium");
String result = "";
if (hasSilicon && hasRadium) {
result = "BOTH";
} else if (hasSilicon) {
result = "SILICON";
} else if (hasRadium) {
result = "RADIUM";
}
return result;
i have used predicate in filter to for radium and silicon and using the resulted set i am printing the result.
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Element> elementss = new ArrayList<>();
Set<String> stringSet = elementss.stream().map(e -> e.getName())
.filter(string -> (string.equals("Radium") || string.equals("Silicon")))
.collect(Collectors.toSet());
if(stringSet.size()==2){
System.out.println("both");
}else if(stringSet.size()==1){
System.out.println(stringSet);
}else{
System.out.println(" ");
}
}
}
You could save a few lines if you use regex, but I doubt if it is better than the other answers:
String resolve(List<Element> elements) {
String result = elements.stream()
.map(Element::getName)
.map(String::toUpperCase)
.filter(str -> str.matches("RADIUM|SILICON"))
.sorted()
.collect(Collectors.joining());
return result.matches("RADIUMSILICON") ? "BOTH" : result;
}

Grouping by ranges with Java 8 Stream API

As the title implies, I am trying to is groupingBy to group a list of objects by range of their "power levels". I am very new to Java Stream API, so I don't quite know my way around it. The code I have written won't compile and I'm just not certain why, I think I have the right idea, but it's not working. Any help would be greatly appreciated! You can find my failed attempt below.
This is the groupingBy attempt:
Map<BigDecimal, List<Employee>> ranges = ObjectList.stream()
.collect(Collectors.groupingBy(a -> {
if(a.powerLevel < 2500) { a.powerLevel = LOWER;}
else if(a.powerLevel >10000) {a.powerLevel = UPPER;}
else {a.PowerLevel = MIDDLE;}
}
));
This is my enum:
enum salaryRanges {
LOWER, MIDDLE, UPPER
}
PS:I tried to see if I could make an enum with ranges, but from what I've read that it not possible in Java.
I would recommended to move logic into different method that defines range
public static salaryRanges getRange(Employee employee) {
if(employee.powerLevel < 2500){
return salaryRanges.LOWER;
}
if(employee.powerLevel > 10000){
return salaryRanges.UPPER;
}
return salaryRanges.MIDDLE;
}
Then use Collectors.groupingBy
Map<salaryRanges,List<Employee>> res = list.stream()
.collect(Collectors.groupingBy(JavaMain::getRange));
You can also do it in another way as #Andreas suggested, by moving defining salary range logic into enum
enum salaryRanges {
LOWER, MIDDLE, UPPER
public static salaryRanges fromPowerLevel(int powerLevel){
if(powerLevel<2500){
return salaryRanges.LOWER;
}
if(powerLevel>10000){
return salaryRanges.UPPER;
}
return salaryRanges.MIDDLE;
}
}
And then calling that method using lambda expression
Map<salaryRanges,List<Employee>> res = list.stream()
.collect(Collectors.groupingBy(emp->salaryRanges.fromPowerLevel(emp.powerLevel)));

Java 8 Stream Collectors - Collector to create a Map with objects in multiple buckets

The following code works and is readable but it seems to me I have intermediate operations that feel like they shouldn't be necessary. I've written this simplified version as the actual code is part of a much larger process.
I've got a Collection of Widget, each with a name and multiple types (indicated by constants of the WidgetType enum). These multiple types are gettable as a Stream<WidgetType> though, if necessary, I could return those as some other type. (For various reasons, it is strongly desirable that these be returned as a Stream<WidgetType> because of how these widgets are used later in the actual code.)
These widgets are added to an EnumMap<WidgetType, List<Widget>> which is, later, translated into an EnumMap<WidgetType, Widget[]>.
If each Widget only had a single WidgetType, this would be a trivial solve but, since any Widget could have 1 or more types, I am tripping all over myself with the syntax of the Collectors.groupingBy() method (and its overloads).
Here's the code example, again, fully functional and gives me the exact result I need.
class StackOverFlowExample {
private final Map<WidgetType, Widget[]> widgetMap = new EnumMap<>(WidgetType.class);
public static void main(String[] args) { new StackOverFlowExample(); }
StackOverFlowExample() {
Collection<Widget> widgetList = getWidgetsFromWhereverWidgetsComeFrom();
{
final Map<WidgetType, List<Widget>> intermediateMap = new EnumMap<>(WidgetType.class);
widgetList.forEach(w ->
w.getWidgetTypes().forEach(wt -> {
intermediateMap.putIfAbsent(wt, new ArrayList<>());
intermediateMap.get(wt).add(w);
})
);
intermediateMap.entrySet().forEach(e -> widgetMap.put(e.getKey(), e.getValue().toArray(new Widget[0])));
}
Arrays.stream(WidgetType.values()).forEach(wt -> System.out.println(wt + ": " + Arrays.toString(widgetMap.get(wt))));
}
private Collection<Widget> getWidgetsFromWhereverWidgetsComeFrom() {
return Arrays.asList(
new Widget("1st", WidgetType.TYPE_A, WidgetType.TYPE_B),
new Widget("2nd", WidgetType.TYPE_A, WidgetType.TYPE_C),
new Widget("3rd", WidgetType.TYPE_A, WidgetType.TYPE_D),
new Widget("4th", WidgetType.TYPE_C, WidgetType.TYPE_D)
);
}
}
This outputs:
TYPE_A: [1st, 2nd, 3rd]
TYPE_B: [1st]
TYPE_C: [2nd, 4th]
TYPE_D: [3rd, 4th]
For completeness sake, here's the Widget class and the WidgetType enum:
class Widget {
private final String name;
private final WidgetType[] widgetTypes;
Widget(String n, WidgetType ... wt) { name = n; widgetTypes = wt; }
public String getName() { return name; }
public Stream<WidgetType> getWidgetTypes() { return Arrays.stream(widgetTypes).distinct(); }
#Override public String toString() { return name; }
}
enum WidgetType { TYPE_A, TYPE_B, TYPE_C, TYPE_D }
Any ideas on a better way to execute this logic are welcome. Thanks!
IMHO, the key is to convert a Widget instance to a Stream<Pair<WidgetType, Widget>> instance. Once we have that, we can flatMap a stream of widgets and collect on the resulting stream. Of course we don't have Pair in Java, so have to use AbstractMap.SimpleEntry instead.
widgets.stream()
// Convert a stream of widgets to a stream of (type, widget)
.flatMap(w -> w.getTypes().map(t->new AbstractMap.SimpleEntry<>(t, w)))
// Grouping by the key, and do additional mapping to get the widget
.collect(groupingBy(e->e.getKey(),
mapping(e->e.getValue,
collectingAndThen(toList(), l->l.toArray(new Widget[0])))));
P.S. this is an occasion where IntelliJ's suggestion doesn't shorten a lambda with method reference.
This is a bit convoluted, but it produces the same output, not necessarily in the same order. It uses a static import of java.util.stream.Collectors.*.
widgetMap = widgetList.stream()
.flatMap(w -> w.getWidgetTypes().map(t -> new AbstractMap.SimpleEntry<>(t, w)))
.collect(groupingBy(Map.Entry::getKey, collectingAndThen(mapping(Map.Entry::getValue, toSet()), s -> s.stream().toArray(Widget[]::new))));
Output on my machine:
TYPE_A: [1st, 3rd, 2nd]
TYPE_B: [1st]
TYPE_C: [2nd, 4th]
TYPE_D: [3rd, 4th]

Analog of everyItem() from Hamcrest in AssertJ

Is there analog of everyItem() from Hamcrest in AssertJ?
I have a list of emails and need to do Assertion to check that each email contains substring "alex". Currently the only way I can do it with AssertJ is as follows:
List<String> actual = Arrays.asList("alex#gmail.com", "alex1#gmail.com", "ale2#hotmail.com", "bred#gmail.com");
SoftAssertions softly = new SoftAssertions();
for(String email: actual ) {
softly.assertThat(email).contains("alex");
}
softly.assertAll();
Can be done without Soft Assertions there as well, but I'd prefer to check all the item of the list.
Is there any more compact way to do so? To be specific, is there a way in AssertJ to check each item of the list to match a substring?
In Hamcrest I can do it in one line:
assertThat(actual, everyItem(containsString("alex")));
But in AssertJ looks like in any way I have to manually iterate through the list.
Assertj 3.6.0 introduced the allSatisfy assertion, which allows you to perform scoped assertions on each element of the iterable.
Therefore you could do what you want with
assertThat(actual).allSatisfy(elem -> assertThat(elem).contains("alex"));
I found 2 solutions:
1) use java 8
actual.forEach( val -> softly.assertThat(val).contains("alex"));
2) make an utility class
public class AssertUtils {
public static Condition<String> ContainsCondition(String val) {
return new Condition<String>() {
#Override
public boolean matches(String value) {
return value.contains(val);
}
};
}
}
and use it:
softly.assertThat(actual).are(AssertUtils.ContainsCondition("alex"));
You can build AssertJ condition with predicate and use are/have assertion:
#Test
public void condition_built_with_predicate_example() {
Condition<String> fairyTale = new Condition<String>(s -> s.startsWith("Once upon a time"), "a %s tale", "fairy");
String littleRedCap = "Once upon a time there was a dear little girl ...";
String cindirella = "Once upon a time there was a ...";
assertThat(asList(littleRedCap, cindirella)).are(fairyTale);
}
Edit: As pointed by Dan I would now use allSatisfy.
I prefer to use this form of allMatch as follow:
assertThat(movies).extracting("title").allMatch(s -> s.toString().contains("the"));
I just rely on Java 8 stream functionality for that kind of stuff:
assertThat(actual.stream().allMatch(s -> s.contains("alex"))).isTrue();

Using Java 8's Optional with Stream::flatMap

The new Java 8 stream framework and friends make for some very concise Java code, but I have come across a seemingly-simple situation that is tricky to do concisely.
Consider a List<Thing> things and method Optional<Other> resolve(Thing thing). I want to map the Things to Optional<Other>s and get the first Other.
The obvious solution would be to use things.stream().flatMap(this::resolve).findFirst(), but flatMap requires that you return a stream, and Optional doesn't have a stream() method (or is it a Collection or provide a method to convert it to or view it as a Collection).
The best I can come up with is this:
things.stream()
.map(this::resolve)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
But that seems awfully long-winded for what seems like a very common case.
Anyone have a better idea?
Java 9
Optional.stream has been added to JDK 9. This enables you to do the following, without the need of any helper method:
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(Optional::stream)
.findFirst();
Java 8
Yes, this was a small hole in the API, in that it's somewhat inconvenient to turn an Optional<T> into a zero-or-one length Stream<T>. You could do this:
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
.findFirst();
Having the ternary operator inside the flatMap is a bit cumbersome, though, so it might be better to write a little helper function to do this:
/**
* Turns an Optional<T> into a Stream<T> of length zero or one depending upon
* whether a value is present.
*/
static <T> Stream<T> streamopt(Optional<T> opt) {
if (opt.isPresent())
return Stream.of(opt.get());
else
return Stream.empty();
}
Optional<Other> result =
things.stream()
.flatMap(t -> streamopt(resolve(t)))
.findFirst();
Here, I've inlined the call to resolve() instead of having a separate map() operation, but this is a matter of taste.
I'm adding this second answer based on a proposed edit by user srborlongan to my other answer. I think the technique proposed was interesting, but it wasn't really suitable as an edit to my answer. Others agreed and the proposed edit was voted down. (I wasn't one of the voters.) The technique has merit, though. It would have been best if srborlongan had posted his/her own answer. This hasn't happened yet, and I didn't want the technique to be lost in the mists of the StackOverflow rejected edit history, so I decided to surface it as a separate answer myself.
Basically the technique is to use some of the Optional methods in a clever way to avoid having to use a ternary operator (? :) or an if/else statement.
My inline example would be rewritten this way:
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
An my example that uses a helper method would be rewritten this way:
/**
* Turns an Optional<T> into a Stream<T> of length zero or one depending upon
* whether a value is present.
*/
static <T> Stream<T> streamopt(Optional<T> opt) {
return opt.map(Stream::of)
.orElseGet(Stream::empty);
}
Optional<Other> result =
things.stream()
.flatMap(t -> streamopt(resolve(t)))
.findFirst();
COMMENTARY
Let's compare the original vs modified versions directly:
// original
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
// modified
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
The original is a straightforward if workmanlike approach: we get an Optional<Other>; if it has a value, we return a stream containing that value, and if it has no value, we return an empty stream. Pretty simple and easy to explain.
The modification is clever and has the advantage that it avoids conditionals. (I know that some people dislike the ternary operator. If misused it can indeed make code hard to understand.) However, sometimes things can be too clever. The modified code also starts off with an Optional<Other>. Then it calls Optional.map which is defined as follows:
If a value is present, apply the provided mapping function to it, and if the result is non-null, return an Optional describing the result. Otherwise return an empty Optional.
The map(Stream::of) call returns an Optional<Stream<Other>>. If a value was present in the input Optional, the returned Optional contains a Stream that contains the single Other result. But if the value was not present, the result is an empty Optional.
Next, the call to orElseGet(Stream::empty) returns a value of type Stream<Other>. If its input value is present, it gets the value, which is the single-element Stream<Other>. Otherwise (if the input value is absent) it returns an empty Stream<Other>. So the result is correct, the same as the original conditional code.
In the comments discussing on my answer, regarding the rejected edit, I had described this technique as "more concise but also more obscure". I stand by this. It took me a while to figure out what it was doing, and it also took me a while to write up the above description of what it was doing. The key subtlety is the transformation from Optional<Other> to Optional<Stream<Other>>. Once you grok this it makes sense, but it wasn't obvious to me.
I'll acknowledge, though, that things that are initially obscure can become idiomatic over time. It might be that this technique ends up being the best way in practice, at least until Optional.stream gets added (if it ever does).
UPDATE: Optional.stream has been added to JDK 9.
You cannot do it more concise as you are already doing.
You claim that you do not want .filter(Optional::isPresent) and .map(Optional::get).
This has been resolved by the method #StuartMarks describes, however as a result you now map it to an Optional<T>, so now you need to use .flatMap(this::streamopt) and a get() in the end.
So it still consists of two statements and you can now get exceptions with the new method! Because, what if every optional is empty? Then the findFirst() will return an empty optional and your get() will fail!
So what you have:
things.stream()
.map(this::resolve)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
is actually the best way to accomplish what you want, and that is you want to save the result as a T, not as an Optional<T>.
I took the liberty of creating a CustomOptional<T> class that wraps the Optional<T> and provides an extra method, flatStream(). Note that you cannot extend Optional<T>:
class CustomOptional<T> {
private final Optional<T> optional;
private CustomOptional() {
this.optional = Optional.empty();
}
private CustomOptional(final T value) {
this.optional = Optional.of(value);
}
private CustomOptional(final Optional<T> optional) {
this.optional = optional;
}
public Optional<T> getOptional() {
return optional;
}
public static <T> CustomOptional<T> empty() {
return new CustomOptional<>();
}
public static <T> CustomOptional<T> of(final T value) {
return new CustomOptional<>(value);
}
public static <T> CustomOptional<T> ofNullable(final T value) {
return (value == null) ? empty() : of(value);
}
public T get() {
return optional.get();
}
public boolean isPresent() {
return optional.isPresent();
}
public void ifPresent(final Consumer<? super T> consumer) {
optional.ifPresent(consumer);
}
public CustomOptional<T> filter(final Predicate<? super T> predicate) {
return new CustomOptional<>(optional.filter(predicate));
}
public <U> CustomOptional<U> map(final Function<? super T, ? extends U> mapper) {
return new CustomOptional<>(optional.map(mapper));
}
public <U> CustomOptional<U> flatMap(final Function<? super T, ? extends CustomOptional<U>> mapper) {
return new CustomOptional<>(optional.flatMap(mapper.andThen(cu -> cu.getOptional())));
}
public T orElse(final T other) {
return optional.orElse(other);
}
public T orElseGet(final Supplier<? extends T> other) {
return optional.orElseGet(other);
}
public <X extends Throwable> T orElseThrow(final Supplier<? extends X> exceptionSuppier) throws X {
return optional.orElseThrow(exceptionSuppier);
}
public Stream<T> flatStream() {
if (!optional.isPresent()) {
return Stream.empty();
}
return Stream.of(get());
}
public T getTOrNull() {
if (!optional.isPresent()) {
return null;
}
return get();
}
#Override
public boolean equals(final Object obj) {
return optional.equals(obj);
}
#Override
public int hashCode() {
return optional.hashCode();
}
#Override
public String toString() {
return optional.toString();
}
}
You will see that I added flatStream(), as here:
public Stream<T> flatStream() {
if (!optional.isPresent()) {
return Stream.empty();
}
return Stream.of(get());
}
Used as:
String result = Stream.of("a", "b", "c", "de", "fg", "hij")
.map(this::resolve)
.flatMap(CustomOptional::flatStream)
.findFirst()
.get();
You still will need to return a Stream<T> here, as you cannot return T, because if !optional.isPresent(), then T == null if you declare it such, but then your .flatMap(CustomOptional::flatStream) would attempt to add null to a stream and that is not possible.
As example:
public T getTOrNull() {
if (!optional.isPresent()) {
return null;
}
return get();
}
Used as:
String result = Stream.of("a", "b", "c", "de", "fg", "hij")
.map(this::resolve)
.map(CustomOptional::getTOrNull)
.findFirst()
.get();
Will now throw a NullPointerException inside the stream operations.
Conclusion
The method you used, is actually the best method.
A slightly shorter version using reduce:
things.stream()
.map(this::resolve)
.reduce(Optional.empty(), (a, b) -> a.isPresent() ? a : b );
You could also move the reduce function to a static utility method and then it becomes:
.reduce(Optional.empty(), Util::firstPresent );
As my previous answer appeared not to be very popular, I will give this another go.
A short answer:
You are mostly on a right track. The shortest code to get to your desired output I could come up with is this:
things.stream()
.map(this::resolve)
.filter(Optional::isPresent)
.findFirst()
.flatMap( Function.identity() );
This will fit all your requirements:
It will find first response that resolves to a nonempty Optional<Result>
It calls this::resolve lazily as needed
this::resolve will not be called after first non-empty result
It will return Optional<Result>
Longer answer
The only modification compared to OP initial version was that I removed .map(Optional::get) before call to .findFirst() and added .flatMap(o -> o) as the last call in the chain.
This has a nice effect of getting rid of the double-Optional, whenever stream finds an actual result.
You can't really go any shorter than this in Java.
The alternative snippet of code using the more conventional for loop technique is going to be about same number of lines of code and have more or less same order and number of operations you need to perform:
Calling this.resolve,
filtering based on Optional.isPresent
returning the result and
some way of dealing with negative result (when nothing was found)
Just to prove that my solution works as advertised, I wrote a small test program:
public class StackOverflow {
public static void main( String... args ) {
try {
final int integer = Stream.of( args )
.peek( s -> System.out.println( "Looking at " + s ) )
.map( StackOverflow::resolve )
.filter( Optional::isPresent )
.findFirst()
.flatMap( o -> o )
.orElseThrow( NoSuchElementException::new )
.intValue();
System.out.println( "First integer found is " + integer );
}
catch ( NoSuchElementException e ) {
System.out.println( "No integers provided!" );
}
}
private static Optional<Integer> resolve( String string ) {
try {
return Optional.of( Integer.valueOf( string ) );
}
catch ( NumberFormatException e )
{
System.out.println( '"' + string + '"' + " is not an integer");
return Optional.empty();
}
}
}
(It does have few extra lines for debugging and verifying that only as many calls to resolve as needed...)
Executing this on a command line, I got the following results:
$ java StackOferflow a b 3 c 4
Looking at a
"a" is not an integer
Looking at b
"b" is not an integer
Looking at 3
First integer found is 3
Late to the party, but what about
things.stream()
.map(this::resolve)
.filter(Optional::isPresent)
.findFirst().get();
You can get rid of the last get() if you create a util method to convert optional to stream manually:
things.stream()
.map(this::resolve)
.flatMap(Util::optionalToStream)
.findFirst();
If you return stream right away from your resolve function, you save one more line.
I'd like to promote factory methods for creating helpers for functional APIs:
Optional<R> result = things.stream()
.flatMap(streamopt(this::resolve))
.findFirst();
The factory method:
<T, R> Function<T, Stream<R>> streamopt(Function<T, Optional<R>> f) {
return f.andThen(Optional::stream); // or the J8 alternative:
// return t -> f.apply(t).map(Stream::of).orElseGet(Stream::empty);
}
Reasoning:
As with method references in general, compared to lambda expressions, you can't accidentaly capture a variable from the accessible scope, like:
t -> streamopt(resolve(o))
It's composable, you can e.g. call Function::andThen on the factory method result:
streamopt(this::resolve).andThen(...)
Whereas in the case of a lambda, you'd need to cast it first:
((Function<T, Stream<R>>) t -> streamopt(resolve(t))).andThen(...)
If you're stuck with Java 8 but have access to Guava 21.0 or newer, you can use Streams.stream to convert an optional into a stream.
Thus, given
import com.google.common.collect.Streams;
you can write
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(Streams::stream)
.findFirst();
If you don't mind to use a third party library you may use Javaslang. It is like Scala, but implemented in Java.
It comes with a complete immutable collection library that is very similar to that known from Scala. These collections replace Java's collections and Java 8's Stream. It also has its own implementation of Option.
import javaslang.collection.Stream;
import javaslang.control.Option;
Stream<Option<String>> options = Stream.of(Option.some("foo"), Option.none(), Option.some("bar"));
// = Stream("foo", "bar")
Stream<String> strings = options.flatMap(o -> o);
Here is a solution for the example of the initial question:
import javaslang.collection.Stream;
import javaslang.control.Option;
public class Test {
void run() {
// = Stream(Thing(1), Thing(2), Thing(3))
Stream<Thing> things = Stream.of(new Thing(1), new Thing(2), new Thing(3));
// = Some(Other(2))
Option<Other> others = things.flatMap(this::resolve).headOption();
}
Option<Other> resolve(Thing thing) {
Other other = (thing.i % 2 == 0) ? new Other(i + "") : null;
return Option.of(other);
}
}
class Thing {
final int i;
Thing(int i) { this.i = i; }
public String toString() { return "Thing(" + i + ")"; }
}
class Other {
final String s;
Other(String s) { this.s = s; }
public String toString() { return "Other(" + s + ")"; }
}
Disclaimer: I'm the creator of Javaslang.
Null is supported by the Stream provided My library abacus-common. Here is code:
Stream.of(things).map(e -> resolve(e).orNull()).skipNull().first();
What about that?
private static List<String> extractString(List<Optional<String>> list) {
List<String> result = new ArrayList<>();
list.forEach(element -> element.ifPresent(result::add));
return result;
}
https://stackoverflow.com/a/58281000/3477539
Most likely You are doing it wrong.
Java 8 Optional is not meant to be used in this manner. It is usually only reserved for terminal stream operations that may or may not return a value, like find for example.
In your case it might be better to first try to find a cheap way to filter out those items that are resolvable and then get the first item as an optional and resolve it as a last operation. Better yet - instead of filtering, find the first resolvable item and resolve it.
things.filter(Thing::isResolvable)
.findFirst()
.flatMap(this::resolve)
.get();
Rule of thumb is that you should strive to reduce number of items in the stream before you transform them to something else. YMMV of course.

Categories

Resources