Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List::contains with comparator

Is there any way (method, lambda, or elegant construct) to find an element in a list based on a given comparator?

I wrote a method like this:

private static <T> boolean contains(List<T> list, T item, Comparator<? super T> comparator) {
    return list.stream()
            .anyMatch(listItem -> comparator.compare(listItem, item) == 0
            );
}

But I'm looking to replace it with something that would be more elegant.

I don't want to add any dependency, so no Guava, "commons", etc. I'm really looking for a pretty way to do this in Java 8.

EDIT: Some example of what I'd consider more elegant (here is the using code):

// sadly, this method doesn't exist
// nor is there a static one in Collections
// but maybe you can think of another way?
if (list.containsSame(item, comparator)) {
    // ...
}
like image 958
ymajoros Avatar asked Dec 03 '14 10:12

ymajoros


2 Answers

As far as I know, there is no built-in functionality directly addressing this task. So since you can’t avoid creating utility methods (if you want reduce code duplication), it’s worth thinking about which kind of utility method could be useful in other scenarios as well.

E.g. if it was my project I knew there are almost always methods for partial function application flying around, like:

public static <T,U,R> Function<U,R> bind(BiFunction<T,U,R> f, T t) {
    return u -> f.apply(t, u);
}

Utilizing this existing method, a solution could look like:

static <T> boolean contains(List<T> list, T item, Comparator<? super T> comparator) {
  return list.stream().map(bind(comparator::compare, item))
                      .anyMatch(Predicate.isEqual(0));
}

But this is not necessarily the best solution.

Another approach could be to have a method for converting a Comparator into an equality BiPredicate and a utility method for partial application of a BiPredicate:

public static <T> BiPredicate<T,T> match(Comparator<T> f) {
    return (a,b)->f.compare(a, b)==0;
}
public static <T,U> Predicate<U> bind(BiPredicate<T,U> f, T t) {
    return u -> f.test(t, u);
}

Then the contains method becomes as simple as

static <T> boolean contains(List<T> list, T item, Comparator<? super T> comparator) {
  return list.stream().anyMatch(bind(match(comparator), item));
}

But this is only a simplification if the utility methods can be used at other places of your project as well. On the other hand, they are of such a general nature that similar methods might be added as default methods to the function interfaces in a subsequent Java release. In this case your code using such utility methods is prepared for migration to that newer version.

like image 67
Holger Avatar answered Sep 24 '22 03:09

Holger


You can use the next methods from the commons-collections version 4+:

  • IterableUtils.contains(Iterable<? extends E> iterable, E object, Equator<? super E> equator) - Checks if the object is contained in the given iterable.
  • IterableUtils.matchesAny(Iterable<E> iterable, Predicate<? super E> predicate) - Answers true if a predicate is true for any element of the iterable.
like image 44
Igor Luzhanov Avatar answered Sep 24 '22 03:09

Igor Luzhanov