As it almost but not really matches Optional, maybe you might reconsider the logic:
Java 8 has a limited expressiveness:
Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);
System.out.println(element.orElse(DEFAULT_ELEM));
Here the map might restrict the view on the element:
element.map(el -> el.mySpecialView()).ifPresent(System.out::println);
Java 9:
element.ifPresentOrElse(el -> System.out.println("Present " + el,
() -> System.out.println("Not present"));
In general the two branches are asymmetric.
Answer from Joop Eggen on Stack OverflowAs it almost but not really matches Optional, maybe you might reconsider the logic:
Java 8 has a limited expressiveness:
Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);
System.out.println(element.orElse(DEFAULT_ELEM));
Here the map might restrict the view on the element:
element.map(el -> el.mySpecialView()).ifPresent(System.out::println);
Java 9:
element.ifPresentOrElse(el -> System.out.println("Present " + el,
() -> System.out.println("Not present"));
In general the two branches are asymmetric.
It's called a 'fluent interface'. Simply change the return type and return this; to allow you to chain the methods:
public MyClass ifExist(Consumer<Element> consumer) {
if (exist()) {
consumer.accept(this);
}
return this;
}
public MyClass ifNotExist(Consumer<Element> consumer) {
if (!exist()) {
consumer.accept(this);
}
return this;
}
You could get a bit fancier and return an intermediate type:
interface Else<T>
{
public void otherwise(Consumer<T> consumer); // 'else' is a keyword
}
class DefaultElse<T> implements Else<T>
{
private final T item;
DefaultElse(final T item) { this.item = item; }
public void otherwise(Consumer<T> consumer)
{
consumer.accept(item);
}
}
class NoopElse<T> implements Else<T>
{
public void otherwise(Consumer<T> consumer) { }
}
public Else<MyClass> ifExist(Consumer<Element> consumer) {
if (exist()) {
consumer.accept(this);
return new NoopElse<>();
}
return new DefaultElse<>(this);
}
Sample usage:
element.ifExist(el -> {
//do something
})
.otherwise(el -> {
//do something else
});
How to perform nested 'if' statements using Java 8/lambda? - Stack Overflow
Using lambda expressions for if statements - Code with Mosh Forum
Java Lambda Expression for if condition - not expected here - Stack Overflow
if statement - Java 8 Lambda To Handle for > if > else - Stack Overflow
Videos
The essential observation here is that your problem involves a non-isomorphic transformation: a single input element may map to zero, one, or two output elements. Whenever you notice this, you should immediately start looking for a solution which involves flatMap instead of map because that's the only way to achieve such a general transformation. In your particular case you can first apply filter for a one-to-zero element mapping, then flatMap for one-to-two mapping:
List<Integer> result =
IntStream.rangeClosed(1, 10)
.filter(i -> 10 % i == 0)
.flatMap(i -> i == 5 ? IntStream.of(i) : IntStream.of(i, 10 / i))
.boxed()
.collect(toList());
(assuming import static java.util.stream.Collectors.toList)
You could declare a body for a lambda. For example:
Runnable run = () -> System.out.println("Hey");
Could be
Runnable run = () -> {
System.out.println("Hey");
};
Within that body, you can create nested statements:
Runnable run = () -> {
int num = 5;
if(num == 5) {
System.out.println("Hey");
}
};
You can write, given for instance a List<Boolean>:
if (!list.stream().allMatch(x -> x)) {
// not every member is true
}
Or:
if (list.stream().anyMatch(x -> !x)) {
// at least one member is false
}
If you have an array of booleans, then use Arrays.stream() to obtain a stream out of it instead.
More generally, for a Stream providing elements of (generic) type X, you have to provide a Predicate<? super X> to .{all,any}Match() (either a "full" predicate, or a lambda, or a method reference -- many things go). The return value of these methods are self explanatory -- I think.
Now, to count elements which obey a certain predicate, you have .count(), which you can combine with .filter() -- which also takes (whatever is) a Predicate as an argument. For instance checking if you have more than 2 elements in a List<String> whose length is greater than 5 you'd do:
if (list.stream().filter(s -> s.length() > 5).count() > 2L) {
// Yup...
}
Your problem
Your current problem is that you use directly a lambda expression. Lambdas are instances of functional interfaces. Your lambda does not have the boolean type, that's why your if does not accept it.
This special case's solution
You can use a stream from your collections of booleans here.
if (bools.stream().allMatch((Boolean b)->b)) {
// do something
}
It is actually much more powerful than this, but this does the trick I believe.
General hint
Basically, since you want an if condition, you want a boolean result.
Since your result depends on a collection, you can use Java 8 streams on collections.
Java 8 streams allow you to do many operations on a collection, and finish with a terminal operation. You can do whatever complicated stuff you want with Stream's non-terminal operations. In the end you need one of 2 things:
- use a terminal operation that returns a
boolean(such asallMatch,anyMatch...), and you're done - use any terminal operation, but use it in a
booleanexpression, such asmyStream.filter(...).limit(...).count() > 2
You should have a look at your possibilities in this Stream documentation or this one.
Some example of lambda usage:
You can group your sites by active and not active, after that you can perform some logic to both. Put the big logic into a method and then call in the map() function.
You can use filter() as well to get only a part of the stream.
public class Lambda {
static class Site {
public boolean isActive;
public boolean otherCondition;
public boolean isActive() {
return isActive;
}
public boolean isOtherCondition() {
return otherCondition;
}
public void setProperty(String property) {
}
}
public static Site someOperation(Site site) {
//some code
return site;
}
public static void main(String[] args) {
ArrayList<Site> sites = new ArrayList<>();
Map<Boolean, List<Site>> groupedSites = sites.stream().collect(Collectors.groupingBy(Site::isActive));
groupedSites.get(Boolean.TRUE).stream().filter(Site::isOtherCondition).map(Lambda::someOperation).forEach(p -> p.setProperty("something"));
}
}
I would put the complex logic into a separate method and reference to it in the map(…).
sites.stream()
.map(Foo::bar)
…;
This would use a method in a class Foo that could look like this:
public static String bar(Site site)
{
return site.toString();
}
Of course you can use arbitrary logic in the method but it should be free of side effects.
And yes, if the method used for mapping is thread safe, you can use parallel() to run the mappings in parallel.