Archives June 2022

Logging in predicates 2 – Functional style programming – extending API

Exposing constainsAll/Any() via an extension of Stream

The previous solution can be considered more like a hack. A more logical and realistic solution will consist of extending the built-in Stream API and adding our containsAll/Any() methods as teammates next to the Stream operations. So, the implementation starts as follows:

@SuppressWarnings(“unchecked”)
public interface Streams<T> extends Stream<T> {      
  …
}

Before implementing the containsAll/Any() methods, we need to handle some aspects resulting from extending the Stream interface. First, we need to override in Streams each of the Stream methods. Since the Stream interface has a lot of methods, we list here only a few of them:

@Override
public Streams<T> filter(Predicate<? super T> predicate);
@Override
public <R> Streams<R> map(
  Function<? super T, ? extends R> mapper);

@Override
public T reduce(T identity, BinaryOperator<T> accumulator);

@Override
default boolean isParallel() {
  return false;
}

@Override
default Streams<T> parallel() {
  throw new UnsupportedOperationException(
    “Not supported yet.”); // or, return this
}
@Override
default Streams<T> unordered() {
  throw new UnsupportedOperationException(
    “Not supported yet.”); // or, return this
}

@Override
default Streams<T> sequential() {
  return this;
}

Since Streams can handle only sequential streams (parallelism is not supported), we can implement the isParallel(), parallel(), unordered(), and sequential() methods as default methods directly in Streams.Next, in order to use our Streams, we need a from(Stream s) method that is capable to wrap the given Stream as follows:

static <T> Streams<T> from(Stream<? extends T> stream) {
  if (stream == null) {
    return from(Stream.empty());
  }
  if (stream instanceof Streams) {
    return (Streams<T>) stream;
  }
  return new StreamsWrapper<>(stream);
}

The StreamsWrapper is a class that wraps the current Stream into sequential Streams. StreamsWrapper implements Streams, so it has to override all the Streams methods and properly wrap the Stream to Streams. Because Streams has quite a lot of methods (as a consequence of extending Stream), we list here only a few of them (the rest are available in the bundled code):

@SuppressWarnings(“unchecked”)
public class StreamsWrapper<T> implements Streams<T> {
  private final Stream<? extends T> delegator;
  public StreamsWrapper(Stream<? extends T> delegator) {
    this.delegator = delegator.sequential();
  }     
  @Override
  public Streams<T> filter(Predicate<? super T> predicate) {      
    return Streams.from(delegator.filter(predicate));
  }
  @Override
  public <R> Streams<R> map(
      Function<? super T, ? extends R> mapper) {
    return Streams.from(delegator.map(mapper));
  }
  …
  @Override
  public T reduce(T identity, BinaryOperator<T> accumulator) {
    return ((Stream<T>) delegator)
      .reduce(identity, accumulator);
  }
  …
}

Finally, we add in Streams the containsAll/Any() methods which are quite straightforward (since Streams extends Stream, we have access to all the Stream goodies without the need to write a stream() hack as in the previous solution). First we add the containsAll() methods:

default boolean contains(T item) {
  return anyMatch(isEqual(item));
}
default boolean containsAll(T… items) {
  return containsAll(Stream.of(items));
}
default boolean containsAll(List<? extends T> items) {
  return containsAll(items.stream());
}
default boolean containsAll(Stream<? extends T> items) {
  Set<? extends T> set = toSet(items);
  if (set.isEmpty()) {
    return true;
  }
  return filter(item -> set.remove(item))
    .anyMatch(any -> set.isEmpty());
}

Second, we add the containsAny() methods:

default boolean containsAny(T… items) {
  return containsAny(Stream.of(items));
}
default boolean containsAny(List<? extends T> items) {
  return containsAny(items.stream());
}
default boolean containsAny(Stream<? extends T> items) {
  Set<? extends T> set = toSet(items);
  if (set.isEmpty()) {
    return false;
  }
  return anyMatch(set::contains);
}

And, the toSet() method that you already know:

static <T> Set<T> toSet(Stream<? extends T> stream) {
  return stream.collect(Collectors.toSet());
}

Mission accomplished! Now, let’s write some examples:

boolean result = Streams.from(cars.stream())
  .filter(car -> car.getBrand().equals(“Mercedes”))
  .contains(car1);
boolean result = Streams.from(cars.stream())
  .containsAll(cars123);
boolean result = Streams.from(cars123.stream())
  .containsAny(cars.stream());

You can find more examples in the bunded code.

Logging in predicates – Functional style programming – extending API

189. Logging in predicates

We already know that the Predicate functional interface relies on its test() method to perform the given check and it returns a boolean value. Let’s suppose that we want to alter the test() method to log the failure cases (the cases that leads to the return of a false value).A quick approach is to write a helper method that sneaks the logging part as follows:

public final class Predicates {
  private static final Logger logger
    = LoggerFactory.getLogger(LogPredicate.class);
  private Predicates() {
    throw new AssertionError(“Cannot be instantiated”);
  }
  public static <T> Predicate<T> testAndLog(
      Predicate<? super T> predicate, String val) {
    return t -> {
      boolean result = predicate.test(t);
      if (!result) {
        logger.warn(predicate + ” don’t match ‘” + val + “‘”);
      }
      return result;
    };
  }
}

Another approach consists of extending the Predicate interface and providing a default method for testing and logging the failure cases as follows:

@FunctionalInterface
public interface LogPredicate<T> extends Predicate<T> {
  Logger logger = LoggerFactory.getLogger(LogPredicate.class);
 
  default boolean testAndLog(T t, String val) {
    boolean result = this.test(t);
    if (!result) {
      logger.warn(t + ” don’t match ‘” + val + “‘”);
    }
    return result;
  }
}

You can practice these examples in the bundled code.

190. Extending Stream with containsAll, containsAny

Let’s assume that we have the following code:

List<Car> cars = Arrays.asList(
  new Car(“Dacia”, “diesel”, 100),
  new Car(“Lexus”, “gasoline”, 300),
  …
  new Car(“Ford”, “electric”, 200)
);
     
Car car1 = new Car(“Lexus”, “diesel”, 300);
Car car2 = new Car(“Ford”, “electric”, 80);
Car car3 = new Car(“Chevrolet”, “electric”, 150);
List<Car> cars123 = List.of(car1, car2, car3);

Next, in the context of a stream pipeline, we want to check if cars contain all/any of the car1, car2, car3, or cars123.The Stream API comes with a rich set of intermediate and final operations but it doesn’t has a built-in containsAll()/containsAny(). So, it is our mission to provide the following final operations:

boolean contains(T item);
boolean containsAll(T… items);
boolean containsAll(List<? extends T> items);
boolean containsAll(Stream<? extends T> items);
boolean containsAny(T… items);
boolean containsAny(List<? extends T> items);
boolean containsAny(Stream<? extends T> items);

We highlighted the methods that get a Stream argument since these methods provide the main logic while the rest of the methods are just calling these ones after converting their arguments to a Stream.

Exposing constainsAll/Any() via a custom interface

The containsAll(Stream<? extends T> items) relies on a Set to accomplish its job as follows (you can challenge yourself to find an alternative implementation):

default boolean containsAll(Stream<? extends T> items) {
  Set<? extends T> set = toSet(items);
  if (set.isEmpty()) {
    return true;
  }
  return stream().filter(item -> set.remove(item))
                 .anyMatch(any -> set.isEmpty());
}

The containsAny(Stream<? extends T> items) relies also on a Set:

default boolean containsAny(Stream<? extends T> items) {
  Set<? extends T> set = toSet(items);
  if (set.isEmpty()) {
    return false;
  }
  return stream().anyMatch(set::contains);
}

The toSet() method is just a helper that collects the Stream items into a Set:

static <T> Set<T> toSet(Stream<? extends T> stream) {
  return stream.collect(Collectors.toSet());
}

Next, let’s sneak these codes into their final place which is a custom interface.As you can see, the containsAll(Stream<? extends T> items) and containsAny(Stream<? extends T> items) are declared as default which means that they are part of an interface. Moreover, both of them call the stream() method that is also part of this interface and hooks the regular Stream.Basically, a quick approach for solving this problem (especially useful in interviews) consists of writing this custom interface (let’s arbitrarily name it Streams) that has access to the original built-in Stream interface as follows:

@SuppressWarnings(“unchecked”)
public interface Streams<T> {
  Stream<T> stream();
  static <T> Streams<T> from(Stream<T> stream) {
    return () -> stream;
  }
  …

Next, the interface exposes a set of default methods that represent the containsAll()/containsAny() flavors as follows:

  default boolean contains(T item) {
    return stream().anyMatch(isEqual(item));
  }
  default boolean containsAll(T… items) {
    return containsAll(Stream.of(items));
  }
  default boolean containsAll(List<? extends T> items) {
    return containsAll(items.stream());
  }
  default boolean containsAll(Stream<? extends T> items) {
    … 
  }
  default boolean containsAny(T… items) {
    return containsAny(Stream.of(items));
  }
  default boolean containsAny(List<? extends T> items) {
    return containsAny(items.stream());
  }
  default boolean containsAny(Stream<? extends T> items) {
    …
  }
  static <T> Set<T> toSet(Stream<? extends T> stream) {
    …
  }
}

Done! Now, we can write different stream pipelines that use the brand new containsAll/Any() operations. For instance, if we want to check if cars contain all items from cars123, we express the stream pipeline as follows:

boolean result = Streams.from(cars.stream())
  .containsAll(cars123);

Here are several more examples:

boolean result = Streams.from(cars.stream())
  .containsAll(car1, car2, car3);
boolean result = Streams.from(cars.stream())
  .containsAny(car1, car2, car3);

Involving more operations can be done as in the following example:

Car car4 = new Car(“Mercedes”, “electric”, 200);      
boolean result = Streams.from(cars.stream()
    .filter(car->car.getBrand().equals(“Mercedes”))
    .distinct()
    .dropWhile(car -> car.getFuel().equals(“gasoline”))
  ).contains(car4);

A more expressive and complete solution to this problem will consist of extending the Stream interface. Let’s do it!